#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 {
static const char* defaultKind = "";
static const char* ambientKind = "ambient";
static const char* transientKind = "transient";
static const char* transientSoloKind = "transient-solo";
static const char* contentKind = "content";
MediaSession::Kind MediaSession::parseKind(const String& kind)
{
if (kind == ambientKind)
return MediaSession::Kind::Ambient;
if (kind == transientKind)
return MediaSession::Kind::Transient;
if (kind == transientSoloKind)
return MediaSession::Kind::TransientSolo;
return MediaSession::Kind::Content;
}
MediaSession::MediaSession(Document& document)
: m_document(document)
{
MediaSessionManager::singleton().addMediaSession(*this);
}
MediaSession::MediaSession(ScriptExecutionContext& context, const String& kind)
: m_document(downcast<Document>(context))
, m_kind(parseKind(kind))
{
if (m_kind == Kind::Content)
m_controls = adoptRef(*new MediaRemoteControls(context));
MediaSessionManager::singleton().addMediaSession(*this);
}
MediaSession::~MediaSession()
{
MediaSessionManager::singleton().removeMediaSession(*this);
}
String MediaSession::kind() const
{
switch (m_kind) {
case MediaSession::Kind::Default:
return defaultKind;
case MediaSession::Kind::Ambient:
return ambientKind;
case MediaSession::Kind::Transient:
return transientKind;
case MediaSession::Kind::TransientSolo:
return transientSoloKind;
case MediaSession::Kind::Content:
return contentKind;
}
}
MediaRemoteControls* MediaSession::controls(bool& isNull)
{
MediaRemoteControls* controls = m_controls.get();
isNull = !controls;
return controls;
}
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::releaseSession()
{
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::togglePlayback()
{
ASSERT(!m_iteratedActiveParticipatingElements);
HashSet<HTMLMediaElement*> activeParticipatingElementsCopy = m_activeParticipatingElements;
m_iteratedActiveParticipatingElements = &activeParticipatingElementsCopy;
while (!activeParticipatingElementsCopy.isEmpty()) {
HTMLMediaElement* element = activeParticipatingElementsCopy.takeAny();
if (element->paused())
element->play();
else
element->pause();
}
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));
}
}
#endif