Media elements are allowed to continue to load media data after navigation
[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 #include "AudioSession.h"
30 #include "Document.h"
31 #include "Logging.h"
32 #include "PlatformMediaSession.h"
33
34 namespace WebCore {
35
36 #if !PLATFORM(MAC)
37 void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
38 {
39 }
40 #endif
41
42 #if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
43
44 #if !PLATFORM(COCOA)
45 static PlatformMediaSessionManager* platformMediaSessionManager = nullptr;
46
47 PlatformMediaSessionManager& PlatformMediaSessionManager::sharedManager()
48 {
49     if (!platformMediaSessionManager)
50         platformMediaSessionManager = new PlatformMediaSessionManager;
51     return *platformMediaSessionManager;
52 }
53
54 PlatformMediaSessionManager* PlatformMediaSessionManager::sharedManagerIfExists()
55 {
56     return platformMediaSessionManager;
57 }
58 #endif // !PLATFORM(COCOA)
59
60 PlatformMediaSessionManager::PlatformMediaSessionManager()
61     : m_systemSleepListener(SystemSleepListener::create(*this))
62 {
63     resetRestrictions();
64 }
65
66 void PlatformMediaSessionManager::resetRestrictions()
67 {
68     m_restrictions[PlatformMediaSession::Video] = NoRestrictions;
69     m_restrictions[PlatformMediaSession::Audio] = NoRestrictions;
70     m_restrictions[PlatformMediaSession::VideoAudio] = NoRestrictions;
71     m_restrictions[PlatformMediaSession::WebAudio] = NoRestrictions;
72     m_restrictions[PlatformMediaSession::MediaStreamCapturingAudio] = NoRestrictions;
73 }
74
75 bool PlatformMediaSessionManager::has(PlatformMediaSession::MediaType type) const
76 {
77     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
78
79     return anyOfSessions([type] (PlatformMediaSession& session, size_t) {
80         return session.mediaType() == type;
81     });
82 }
83
84 bool PlatformMediaSessionManager::activeAudioSessionRequired() const
85 {
86     return anyOfSessions([] (PlatformMediaSession& session, size_t) {
87         return session.activeAudioSessionRequired();
88     });
89 }
90
91 bool PlatformMediaSessionManager::canProduceAudio() const
92 {
93     return anyOfSessions([] (PlatformMediaSession& session, size_t) {
94         return session.canProduceAudio();
95     });
96 }
97
98 int PlatformMediaSessionManager::count(PlatformMediaSession::MediaType type) const
99 {
100     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
101
102     int count = 0;
103     for (auto* session : m_sessions) {
104         if (session->mediaType() == type)
105             ++count;
106     }
107
108     return count;
109 }
110
111 void PlatformMediaSessionManager::beginInterruption(PlatformMediaSession::InterruptionType type)
112 {
113     LOG(Media, "PlatformMediaSessionManager::beginInterruption");
114
115     m_interrupted = true;
116     forEachSession([type] (PlatformMediaSession& session, size_t) {
117         session.beginInterruption(type);
118     });
119     updateSessionState();
120 }
121
122 void PlatformMediaSessionManager::endInterruption(PlatformMediaSession::EndInterruptionFlags flags)
123 {
124     LOG(Media, "PlatformMediaSessionManager::endInterruption");
125
126     m_interrupted = false;
127     forEachSession([flags] (PlatformMediaSession& session, size_t) {
128         session.endInterruption(flags);
129     });
130 }
131
132 void PlatformMediaSessionManager::addSession(PlatformMediaSession& session)
133 {
134     LOG(Media, "PlatformMediaSessionManager::addSession - %p", &session);
135     
136     m_sessions.append(&session);
137     if (m_interrupted)
138         session.setState(PlatformMediaSession::Interrupted);
139
140     if (!m_remoteCommandListener)
141         m_remoteCommandListener = RemoteCommandListener::create(*this);
142
143     if (!m_audioHardwareListener)
144         m_audioHardwareListener = AudioHardwareListener::create(*this);
145
146     updateSessionState();
147 }
148
149 void PlatformMediaSessionManager::removeSession(PlatformMediaSession& session)
150 {
151     LOG(Media, "PlatformMediaSessionManager::removeSession - %p", &session);
152     
153     size_t index = m_sessions.find(&session);
154     if (index == notFound)
155         return;
156
157     if (m_iteratingOverSessions)
158         m_sessions.at(index) = nullptr;
159     else
160         m_sessions.remove(index);
161
162     if (m_sessions.isEmpty() || std::all_of(m_sessions.begin(), m_sessions.end(), std::logical_not<void>())) {
163         m_remoteCommandListener = nullptr;
164         m_audioHardwareListener = nullptr;
165     }
166
167     updateSessionState();
168 }
169
170 void PlatformMediaSessionManager::addRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
171 {
172     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
173     m_restrictions[type] |= restriction;
174 }
175
176 void PlatformMediaSessionManager::removeRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
177 {
178     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
179     m_restrictions[type] &= ~restriction;
180 }
181
182 PlatformMediaSessionManager::SessionRestrictions PlatformMediaSessionManager::restrictions(PlatformMediaSession::MediaType type)
183 {
184     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
185     return m_restrictions[type];
186 }
187
188 bool PlatformMediaSessionManager::sessionWillBeginPlayback(PlatformMediaSession& session)
189 {
190     LOG(Media, "PlatformMediaSessionManager::sessionWillBeginPlayback - %p", &session);
191     
192     setCurrentSession(session);
193
194     PlatformMediaSession::MediaType sessionType = session.mediaType();
195     SessionRestrictions restrictions = m_restrictions[sessionType];
196     if (session.state() == PlatformMediaSession::Interrupted && restrictions & InterruptedPlaybackNotPermitted)
197         return false;
198
199 #if USE(AUDIO_SESSION)
200     if (activeAudioSessionRequired() && !AudioSession::sharedSession().tryToSetActive(true))
201         return false;
202 #endif
203
204     if (m_interrupted)
205         endInterruption(PlatformMediaSession::NoFlags);
206
207     forEachSession([&] (PlatformMediaSession& oneSession, size_t) {
208         if (&oneSession == &session)
209             return;
210         if (oneSession.mediaType() == sessionType
211             && restrictions & ConcurrentPlaybackNotPermitted
212             && oneSession.state() == PlatformMediaSession::Playing)
213             oneSession.pauseSession();
214     });
215
216     updateSessionState();
217     return true;
218 }
219     
220 void PlatformMediaSessionManager::sessionWillEndPlayback(PlatformMediaSession& session)
221 {
222     LOG(Media, "PlatformMediaSessionManager::sessionWillEndPlayback - %p", &session);
223     
224     if (m_sessions.size() < 2)
225         return;
226     
227     size_t pausingSessionIndex = notFound;
228     size_t lastPlayingSessionIndex = notFound;
229     anyOfSessions([&] (PlatformMediaSession& oneSession, size_t i) {
230         if (&oneSession == &session) {
231             pausingSessionIndex = i;
232             return false;
233         }
234         if (oneSession.state() == PlatformMediaSession::Playing) {
235             lastPlayingSessionIndex = i;
236             return false;
237         }
238         return oneSession.state() != PlatformMediaSession::Playing;
239     });
240     if (lastPlayingSessionIndex == notFound || pausingSessionIndex == notFound)
241         return;
242     
243     if (pausingSessionIndex > lastPlayingSessionIndex)
244         return;
245     
246     m_sessions.remove(pausingSessionIndex);
247     m_sessions.insert(lastPlayingSessionIndex, &session);
248     
249     LOG(Media, "PlatformMediaSessionManager::sessionWillEndPlayback - session moved from index %zu to %zu", pausingSessionIndex, lastPlayingSessionIndex);
250 }
251
252 void PlatformMediaSessionManager::setCurrentSession(PlatformMediaSession& session)
253 {
254     LOG(Media, "PlatformMediaSessionManager::setCurrentSession - %p", &session);
255     
256     if (m_sessions.size() < 2)
257         return;
258     
259     size_t index = m_sessions.find(&session);
260     ASSERT(index != notFound);
261     if (!index || index == notFound)
262         return;
263
264     m_sessions.remove(index);
265     m_sessions.insert(0, &session);
266     if (m_remoteCommandListener)
267         m_remoteCommandListener->updateSupportedCommands();
268     
269     LOG(Media, "PlatformMediaSessionManager::setCurrentSession - session moved from index %zu to 0", index);
270 }
271     
272 PlatformMediaSession* PlatformMediaSessionManager::currentSession() const
273 {
274     if (!m_sessions.size())
275         return nullptr;
276
277     return m_sessions[0];
278 }
279
280 Vector<PlatformMediaSession*> PlatformMediaSessionManager::currentSessionsMatching(std::function<bool(const PlatformMediaSession &)> filter)
281 {
282     Vector<PlatformMediaSession*> matchingSessions;
283     forEachSession([&] (PlatformMediaSession& session, size_t) {
284         if (filter(session))
285             matchingSessions.append(&session);
286     });
287     return matchingSessions;
288 }
289     
290 bool PlatformMediaSessionManager::sessionCanLoadMedia(const PlatformMediaSession& session) const
291 {
292     if (session.isSuspended())
293         return false;
294     return session.state() == PlatformMediaSession::Playing || !session.isHidden() || session.shouldOverrideBackgroundLoadingRestriction();
295 }
296
297 void PlatformMediaSessionManager::applicationWillEnterBackground() const
298 {
299     LOG(Media, "PlatformMediaSessionManager::applicationWillEnterBackground");
300
301     if (m_isApplicationInBackground)
302         return;
303
304     m_isApplicationInBackground = true;
305     
306     Vector<PlatformMediaSession*> sessions = m_sessions;
307     forEachSession([&] (PlatformMediaSession& session, size_t) {
308         if (m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
309             session.beginInterruption(PlatformMediaSession::EnteringBackground);
310     });
311 }
312
313 void PlatformMediaSessionManager::applicationDidEnterForeground() const
314 {
315     LOG(Media, "PlatformMediaSessionManager::applicationDidEnterForeground");
316
317     if (!m_isApplicationInBackground)
318         return;
319
320     m_isApplicationInBackground = false;
321
322     Vector<PlatformMediaSession*> sessions = m_sessions;
323     forEachSession([&] (PlatformMediaSession& session, size_t) {
324         if (m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
325             session.endInterruption(PlatformMediaSession::MayResumePlaying);
326     });
327 }
328
329 void PlatformMediaSessionManager::sessionIsPlayingToWirelessPlaybackTargetChanged(PlatformMediaSession& session)
330 {
331     if (!m_isApplicationInBackground || !(m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted))
332         return;
333
334     if (session.state() != PlatformMediaSession::Interrupted)
335         session.beginInterruption(PlatformMediaSession::EnteringBackground);
336 }
337
338 void PlatformMediaSessionManager::sessionCanProduceAudioChanged(PlatformMediaSession&)
339 {
340     updateSessionState();
341 }
342
343 #if !PLATFORM(COCOA)
344 void PlatformMediaSessionManager::updateSessionState()
345 {
346 }
347 #endif
348
349 void PlatformMediaSessionManager::didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType command, const PlatformMediaSession::RemoteCommandArgument* argument)
350 {
351     PlatformMediaSession* activeSession = currentSession();
352     if (!activeSession || !activeSession->canReceiveRemoteControlCommands())
353         return;
354     activeSession->didReceiveRemoteControlCommand(command, argument);
355 }
356
357 bool PlatformMediaSessionManager::supportsSeeking() const
358 {
359     PlatformMediaSession* activeSession = currentSession();
360     if (!activeSession)
361         return false;
362     return activeSession->supportsSeeking();
363 }
364
365 void PlatformMediaSessionManager::systemWillSleep()
366 {
367     if (m_interrupted)
368         return;
369
370     forEachSession([] (PlatformMediaSession& session, size_t) {
371         session.beginInterruption(PlatformMediaSession::SystemSleep);
372     });
373 }
374
375 void PlatformMediaSessionManager::systemDidWake()
376 {
377     if (m_interrupted)
378         return;
379
380     forEachSession([] (PlatformMediaSession& session, size_t) {
381         session.endInterruption(PlatformMediaSession::MayResumePlaying);
382     });
383 }
384
385 void PlatformMediaSessionManager::audioOutputDeviceChanged()
386 {
387     updateSessionState();
388 }
389
390 void PlatformMediaSessionManager::stopAllMediaPlaybackForDocument(const Document* document)
391 {
392     forEachSession([document] (PlatformMediaSession& session, size_t) {
393         if (session.client().hostingDocument() == document)
394             session.pauseSession();
395     });
396 }
397
398 void PlatformMediaSessionManager::stopAllMediaPlaybackForProcess()
399 {
400     forEachSession([] (PlatformMediaSession& session, size_t) {
401         session.stopSession();
402     });
403 }
404
405 void PlatformMediaSessionManager::forEachSession(const Function<void(PlatformMediaSession&, size_t)>& predicate) const
406 {
407     ++m_iteratingOverSessions;
408
409     for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
410         auto session = m_sessions[i];
411         if (!session)
412             continue;
413         predicate(*session, i);
414     }
415
416     --m_iteratingOverSessions;
417     if (!m_iteratingOverSessions)
418         m_sessions.removeAll(nullptr);
419 }
420
421 PlatformMediaSession* PlatformMediaSessionManager::findSession(const Function<bool(PlatformMediaSession&, size_t)>& predicate) const
422 {
423     ++m_iteratingOverSessions;
424
425     PlatformMediaSession* foundSession = nullptr;
426     for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
427         auto session = m_sessions[i];
428         if (!session)
429             continue;
430
431         if (!predicate(*session, i))
432             continue;
433
434         foundSession = session;
435         break;
436     }
437
438     --m_iteratingOverSessions;
439     if (!m_iteratingOverSessions)
440         m_sessions.removeAll(nullptr);
441
442     return foundSession;
443 }
444
445 #endif // ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
446
447 } // namespace WebCore