#include "config.h"
#include "MediaSession.h"
#if ENABLE(MEDIA_SESSION)
#include "Chrome.h"
#include "ChromeClient.h"
#include "Dictionary.h"
#include "Event.h"
#include "HTMLMediaElement.h"
#include "MediaSessionManager.h"
namespace WebCore {
MediaSession::MediaSession(ScriptExecutionContext& context, Kind kind)
: m_document(downcast<Document>(context))
, m_kind(kind)
{
if (m_kind == Kind::Content)
m_controls = MediaRemoteControls::create(context, this);
MediaSessionManager::singleton().addMediaSession(*this);
}
MediaSession::~MediaSession()
{
MediaSessionManager::singleton().removeMediaSession(*this);
if (m_controls)
m_controls->clearSession();
}
MediaRemoteControls* MediaSession::controls()
{
return m_controls.get();
}
void MediaSession::addMediaElement(HTMLMediaElement& element)
{
ASSERT(!m_participatingElements.contains(&element));
m_participatingElements.add(&element);
}
void MediaSession::removeMediaElement(HTMLMediaElement& element)
{
ASSERT(m_participatingElements.contains(&element));
m_participatingElements.remove(&element);
changeActiveMediaElements([&]() {
m_activeParticipatingElements.remove(&element);
});
if (m_iteratedActiveParticipatingElements)
m_iteratedActiveParticipatingElements->remove(&element);
}
void MediaSession::changeActiveMediaElements(std::function<void(void)> worker)
{
if (Page *page = m_document.page()) {
bool hadActiveMediaElements = MediaSessionManager::singleton().hasActiveMediaElements();
worker();
bool hasActiveMediaElements = MediaSessionManager::singleton().hasActiveMediaElements();
if (hadActiveMediaElements != hasActiveMediaElements)
page->chrome().client().hasMediaSessionWithActiveMediaElementsDidChange(hasActiveMediaElements);
} else
worker();
}
void MediaSession::addActiveMediaElement(HTMLMediaElement& element)
{
changeActiveMediaElements([&]() {
m_activeParticipatingElements.add(&element);
});
}
bool MediaSession::isMediaElementActive(HTMLMediaElement& element)
{
return m_activeParticipatingElements.contains(&element);
}
bool MediaSession::hasActiveMediaElements() const
{
return !m_activeParticipatingElements.isEmpty();
}
void MediaSession::setMetadata(const Dictionary& metadata)
{
String title;
metadata.get("title", title);
String artist;
metadata.get("artist", artist);
String album;
metadata.get("album", album);
URL artworkURL;
String artworkPath;
if (metadata.get("artwork", artworkPath))
artworkURL = m_document.completeURL(artworkPath);
m_metadata = MediaSessionMetadata(title, artist, album, artworkURL);
if (Page *page = m_document.page())
page->chrome().client().mediaSessionMetadataDidChange(m_metadata);
}
void MediaSession::deactivate()
{
changeActiveMediaElements([&]() {
while (!m_activeParticipatingElements.isEmpty())
m_activeParticipatingElements.takeAny()->pause();
});
releaseInternal();
}
void MediaSession::releaseInternal()
{
if (m_currentState == State::Idle)
return;
if (!m_activeParticipatingElements.isEmpty())
return;
m_currentState = State::Idle;
}
bool MediaSession::invoke()
{
if (m_currentState == State::Active)
return true;
m_currentState = State::Active;
return true;
}
void MediaSession::handleDuckInterruption()
{
for (auto* element : m_activeParticipatingElements)
element->setShouldDuck(true);
m_currentState = State::Interrupted;
}
void MediaSession::handleUnduckInterruption()
{
for (auto* element : m_activeParticipatingElements)
element->setShouldDuck(false);
m_currentState = State::Active;
}
void MediaSession::handleIndefinitePauseInterruption()
{
safelyIterateActiveMediaElements([](HTMLMediaElement* element) {
element->pause();
});
m_activeParticipatingElements.clear();
m_currentState = State::Idle;
}
void MediaSession::handlePauseInterruption()
{
m_currentState = State::Interrupted;
safelyIterateActiveMediaElements([](HTMLMediaElement* element) {
element->pause();
});
}
void MediaSession::handleUnpauseInterruption()
{
m_currentState = State::Active;
safelyIterateActiveMediaElements([](HTMLMediaElement* element) {
element->play();
});
}
void MediaSession::togglePlayback()
{
safelyIterateActiveMediaElements([](HTMLMediaElement* element) {
if (element->paused())
element->play();
else
element->pause();
});
}
void MediaSession::safelyIterateActiveMediaElements(std::function<void(HTMLMediaElement*)> handler)
{
ASSERT(!m_iteratedActiveParticipatingElements);
HashSet<HTMLMediaElement*> activeParticipatingElementsCopy = m_activeParticipatingElements;
m_iteratedActiveParticipatingElements = &activeParticipatingElementsCopy;
while (!activeParticipatingElementsCopy.isEmpty())
handler(activeParticipatingElementsCopy.takeAny());
m_iteratedActiveParticipatingElements = nullptr;
}
void MediaSession::skipToNextTrack()
{
if (m_controls && m_controls->nextTrackEnabled())
m_controls->dispatchEvent(Event::create(eventNames().nexttrackEvent, false, false));
}
void MediaSession::skipToPreviousTrack()
{
if (m_controls && m_controls->previousTrackEnabled())
m_controls->dispatchEvent(Event::create(eventNames().previoustrackEvent, false, false));
}
void MediaSession::controlIsEnabledDidChange()
{
ASSERT(m_kind == Kind::Content);
ASSERT(m_activeParticipatingElements.size() <= 1);
if (m_activeParticipatingElements.isEmpty())
return;
HTMLMediaElement* element = *m_activeParticipatingElements.begin();
m_document.updateIsPlayingMedia(element->elementID());
}
}
#endif