Ensure media playback is stopped during page close
[WebKit-https.git] / Source / WebCore / platform / audio / PlatformMediaSessionManager.cpp
1 /*
2  * Copyright (C) 2013-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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "PlatformMediaSessionManager.h"
28
29 #if ENABLE(VIDEO)
30
31 #include "AudioSession.h"
32 #include "Document.h"
33 #include "Logging.h"
34 #include "NotImplemented.h"
35 #include "PlatformMediaSession.h"
36
37 namespace WebCore {
38
39 #if !PLATFORM(IOS)
40 PlatformMediaSessionManager& PlatformMediaSessionManager::sharedManager()
41 {
42     DEPRECATED_DEFINE_STATIC_LOCAL(PlatformMediaSessionManager, manager, ());
43     return manager;
44 }
45 #endif
46
47 PlatformMediaSessionManager::PlatformMediaSessionManager()
48     : m_systemSleepListener(SystemSleepListener::create(*this))
49 {
50     resetRestrictions();
51 }
52
53 void PlatformMediaSessionManager::resetRestrictions()
54 {
55     m_restrictions[PlatformMediaSession::Video] = NoRestrictions;
56     m_restrictions[PlatformMediaSession::Audio] = NoRestrictions;
57     m_restrictions[PlatformMediaSession::WebAudio] = NoRestrictions;
58 }
59
60 bool PlatformMediaSessionManager::has(PlatformMediaSession::MediaType type) const
61 {
62     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::WebAudio);
63
64     for (auto* session : m_sessions) {
65         if (session->mediaType() == type)
66             return true;
67     }
68
69     return false;
70 }
71
72 bool PlatformMediaSessionManager::activeAudioSessionRequired() const
73 {
74     for (auto* session : m_sessions) {
75         if (session->mediaType() != PlatformMediaSession::None && session->state() == PlatformMediaSession::State::Playing)
76             return true;
77     }
78     
79     return false;
80 }
81
82 int PlatformMediaSessionManager::count(PlatformMediaSession::MediaType type) const
83 {
84     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::WebAudio);
85
86     int count = 0;
87     for (auto* session : m_sessions) {
88         if (session->mediaType() == type)
89             ++count;
90     }
91
92     return count;
93 }
94
95 void PlatformMediaSessionManager::beginInterruption(PlatformMediaSession::InterruptionType type)
96 {
97     LOG(Media, "PlatformMediaSessionManager::beginInterruption");
98
99     m_interrupted = true;
100     Vector<PlatformMediaSession*> sessions = m_sessions;
101     for (auto* session : sessions)
102         session->beginInterruption(type);
103     updateSessionState();
104 }
105
106 void PlatformMediaSessionManager::endInterruption(PlatformMediaSession::EndInterruptionFlags flags)
107 {
108     LOG(Media, "PlatformMediaSessionManager::endInterruption");
109
110     m_interrupted = false;
111     Vector<PlatformMediaSession*> sessions = m_sessions;
112     for (auto* session : sessions)
113         session->endInterruption(flags);
114 }
115
116 void PlatformMediaSessionManager::addSession(PlatformMediaSession& session)
117 {
118     LOG(Media, "PlatformMediaSessionManager::addSession - %p", &session);
119     
120     m_sessions.append(&session);
121     if (m_interrupted)
122         session.setState(PlatformMediaSession::Interrupted);
123
124     if (!m_remoteCommandListener)
125         m_remoteCommandListener = RemoteCommandListener::create(*this);
126
127     if (!m_audioHardwareListener)
128         m_audioHardwareListener = AudioHardwareListener::create(*this);
129
130     updateSessionState();
131 }
132
133 void PlatformMediaSessionManager::removeSession(PlatformMediaSession& session)
134 {
135     LOG(Media, "PlatformMediaSessionManager::removeSession - %p", &session);
136     
137     size_t index = m_sessions.find(&session);
138     ASSERT(index != notFound);
139     if (index == notFound)
140         return;
141     
142     m_sessions.remove(index);
143
144     if (m_sessions.isEmpty()) {
145         m_remoteCommandListener = nullptr;
146         m_audioHardwareListener = nullptr;
147     }
148
149     updateSessionState();
150 }
151
152 void PlatformMediaSessionManager::addRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
153 {
154     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::WebAudio);
155     m_restrictions[type] |= restriction;
156 }
157
158 void PlatformMediaSessionManager::removeRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
159 {
160     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::WebAudio);
161     m_restrictions[type] &= ~restriction;
162 }
163
164 PlatformMediaSessionManager::SessionRestrictions PlatformMediaSessionManager::restrictions(PlatformMediaSession::MediaType type)
165 {
166     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::WebAudio);
167     return m_restrictions[type];
168 }
169
170 bool PlatformMediaSessionManager::sessionWillBeginPlayback(PlatformMediaSession& session)
171 {
172     LOG(Media, "PlatformMediaSessionManager::sessionWillBeginPlayback - %p", &session);
173     
174     setCurrentSession(session);
175
176     PlatformMediaSession::MediaType sessionType = session.mediaType();
177     SessionRestrictions restrictions = m_restrictions[sessionType];
178     if (session.state() == PlatformMediaSession::Interrupted && restrictions & InterruptedPlaybackNotPermitted)
179         return false;
180
181 #if USE(AUDIO_SESSION)
182     if (activeAudioSessionRequired() && !AudioSession::sharedSession().tryToSetActive(true))
183         return false;
184 #endif
185
186     if (m_interrupted)
187         endInterruption(PlatformMediaSession::NoFlags);
188
189     Vector<PlatformMediaSession*> sessions = m_sessions;
190     for (auto* oneSession : sessions) {
191         if (oneSession == &session)
192             continue;
193         if (oneSession->mediaType() == sessionType && restrictions & ConcurrentPlaybackNotPermitted)
194             oneSession->pauseSession();
195     }
196
197     updateSessionState();
198     return true;
199 }
200     
201 void PlatformMediaSessionManager::sessionWillEndPlayback(PlatformMediaSession& session)
202 {
203     LOG(Media, "PlatformMediaSessionManager::sessionWillEndPlayback - %p", &session);
204     
205     if (m_sessions.size() < 2)
206         return;
207     
208     size_t pausingSessionIndex = notFound;
209     size_t lastPlayingSessionIndex = notFound;
210     for (size_t i = 0; i < m_sessions.size(); ++i) {
211         PlatformMediaSession* oneSession = m_sessions[i];
212         
213         if (oneSession == &session) {
214             pausingSessionIndex = i;
215             continue;
216         }
217         if (oneSession->state() == PlatformMediaSession::Playing) {
218             lastPlayingSessionIndex = i;
219             continue;
220         }
221         if (oneSession->state() != PlatformMediaSession::Playing)
222             break;
223     }
224     if (lastPlayingSessionIndex == notFound || pausingSessionIndex == notFound)
225         return;
226     
227     if (pausingSessionIndex > lastPlayingSessionIndex)
228         return;
229     
230     m_sessions.remove(pausingSessionIndex);
231     m_sessions.insert(lastPlayingSessionIndex, &session);
232     
233     LOG(Media, "PlatformMediaSessionManager::sessionWillEndPlayback - session moved from index %zu to %zu", pausingSessionIndex, lastPlayingSessionIndex);
234 }
235
236 void PlatformMediaSessionManager::setCurrentSession(PlatformMediaSession& session)
237 {
238     LOG(Media, "PlatformMediaSessionManager::setCurrentSession - %p", &session);
239     
240     if (m_sessions.size() < 2)
241         return;
242     
243     size_t index = m_sessions.find(&session);
244     ASSERT(index != notFound);
245     if (!index || index == notFound)
246         return;
247
248     m_sessions.remove(index);
249     m_sessions.insert(0, &session);
250     
251     LOG(Media, "PlatformMediaSessionManager::setCurrentSession - session moved from index %zu to 0", index);
252 }
253     
254 PlatformMediaSession* PlatformMediaSessionManager::currentSession()
255 {
256     if (!m_sessions.size())
257         return nullptr;
258
259     return m_sessions[0];
260 }
261     
262 bool PlatformMediaSessionManager::sessionRestrictsInlineVideoPlayback(const PlatformMediaSession& session) const
263 {
264     PlatformMediaSession::MediaType sessionType = session.presentationType();
265     if (sessionType != PlatformMediaSession::Video)
266         return false;
267
268     return m_restrictions[sessionType] & InlineVideoPlaybackRestricted;
269 }
270
271 bool PlatformMediaSessionManager::sessionCanLoadMedia(const PlatformMediaSession& session) const
272 {
273     return session.state() == PlatformMediaSession::Playing || !session.isHidden() || session.isPlayingToWirelessPlaybackTarget();
274 }
275
276 void PlatformMediaSessionManager::applicationWillEnterBackground() const
277 {
278     LOG(Media, "PlatformMediaSessionManager::applicationWillEnterBackground");
279     Vector<PlatformMediaSession*> sessions = m_sessions;
280     for (auto* session : sessions) {
281         if (m_restrictions[session->mediaType()] & BackgroundProcessPlaybackRestricted)
282             session->beginInterruption(PlatformMediaSession::EnteringBackground);
283     }
284 }
285
286 void PlatformMediaSessionManager::applicationWillEnterForeground() const
287 {
288     LOG(Media, "PlatformMediaSessionManager::applicationWillEnterForeground");
289     Vector<PlatformMediaSession*> sessions = m_sessions;
290     for (auto* session : sessions) {
291         if (m_restrictions[session->mediaType()] & BackgroundProcessPlaybackRestricted)
292             session->endInterruption(PlatformMediaSession::MayResumePlaying);
293     }
294 }
295
296 #if !PLATFORM(COCOA)
297 void PlatformMediaSessionManager::updateSessionState()
298 {
299 }
300 #endif
301
302 void PlatformMediaSessionManager::didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType command)
303 {
304     PlatformMediaSession* activeSession = currentSession();
305     if (!activeSession || !activeSession->canReceiveRemoteControlCommands())
306         return;
307     activeSession->didReceiveRemoteControlCommand(command);
308 }
309
310 void PlatformMediaSessionManager::systemWillSleep()
311 {
312     if (m_interrupted)
313         return;
314
315     for (auto session : m_sessions)
316         session->beginInterruption(PlatformMediaSession::SystemSleep);
317 }
318
319 void PlatformMediaSessionManager::systemDidWake()
320 {
321     if (m_interrupted)
322         return;
323
324     for (auto session : m_sessions)
325         session->endInterruption(PlatformMediaSession::MayResumePlaying);
326 }
327
328 void PlatformMediaSessionManager::audioOutputDeviceChanged()
329 {
330     updateSessionState();
331 }
332
333 void PlatformMediaSessionManager::stopAllMediaPlaybackForDocument(const Document* document)
334 {
335     Vector<PlatformMediaSession*> sessions = m_sessions;
336     for (auto* session : sessions) {
337         if (session->client().hostingDocument() == document)
338             session->pauseSession();
339     }
340 }
341
342 void PlatformMediaSessionManager::stopAllMediaPlaybackForProcess()
343 {
344     Vector<PlatformMediaSession*> sessions = m_sessions;
345     for (auto* session : sessions)
346         session->pauseSession();
347 }
348
349 }
350
351 #endif