summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/mediasession/MediaSession.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/mediasession/MediaSession.cpp')
-rw-r--r--Source/WebCore/Modules/mediasession/MediaSession.cpp282
1 files changed, 282 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/mediasession/MediaSession.cpp b/Source/WebCore/Modules/mediasession/MediaSession.cpp
new file mode 100644
index 000000000..5d141d3bc
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaSession.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MediaSession.h"
+
+#if ENABLE(MEDIA_SESSION)
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "HTMLMediaElement.h"
+#include "MediaSessionManager.h"
+#include "Page.h"
+
+namespace WebCore {
+
+MediaSession::MediaSession(Document& document, Kind kind)
+ : m_document(document)
+ , m_kind(kind)
+{
+ // 4. Media Sessions
+ // 3. If media session's current media session type is "content", then create a new media remote controller for media
+ // session. (Otherwise media session has no media remote controller.)
+ if (m_kind == Kind::Content)
+ m_controls = MediaRemoteControls::create(document, 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 std::optional<Metadata>& optionalMetadata)
+{
+ if (!optionalMetadata)
+ m_metadata = { };
+ else {
+ auto& metadata = optionalMetadata.value();
+ m_metadata = { metadata.title, metadata.artist, metadata.album, m_document.completeURL(metadata.artwork) };
+ }
+
+ if (auto* page = m_document.page())
+ page->chrome().client().mediaSessionMetadataDidChange(m_metadata);
+}
+
+void MediaSession::deactivate()
+{
+ // 5.1.2. Object members
+ // When the deactivate() method is invoked, the user agent must run the following steps:
+ // 1. Let media session be the current media session.
+ // 2. Indefinitely pause all of media session’s audio-producing participants.
+ // 3. Set media session's resume list to an empty list.
+ // 4. Set media session's audio-producing participants to an empty list.
+ changeActiveMediaElements([&]() {
+ while (!m_activeParticipatingElements.isEmpty())
+ m_activeParticipatingElements.takeAny()->pause();
+ });
+
+ // 5. Run the media session deactivation algorithm for media session.
+ releaseInternal();
+}
+
+void MediaSession::releaseInternal()
+{
+ // 6.5. Releasing a media session
+ // 1. If current media session's current state is idle, then terminate these steps.
+ if (m_currentState == State::Idle)
+ return;
+
+ // 2. If current media session still has one or more active participating media elements, then terminate these steps.
+ if (!m_activeParticipatingElements.isEmpty())
+ return;
+
+ // 3. Optionally, based on platform conventions, the user agent must release any currently held platform media focus
+ // for current media session.
+ // 4. Optionally, based on platform conventions, the user agent must remove any previously established ongoing media
+ // interface in the underlying platform’s notifications area and any ongoing media interface in the underlying
+ // platform's lock screen area for current media session, if any.
+ // 5. Optionally, based on platform conventions, the user agent must prevent any hardware and/or software media keys
+ // from controlling playback of current media session's active participating media elements.
+ // 6. Set current media session's current state to idle.
+ m_currentState = State::Idle;
+}
+
+bool MediaSession::invoke()
+{
+ // 4.4 Activating a media session
+ // 1. If we're already ACTIVE then return success.
+ if (m_currentState == State::Active)
+ return true;
+
+ // 2. Optionally, based on platform conventions, request the most appropriate platform-level media focus for media
+ // session based on its current media session type.
+
+ // 3. Run these substeps...
+
+ // 4. Set our current state to ACTIVE and return success.
+ 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()
+{
+ // Media remote controls are only allowed on Content media sessions.
+ ASSERT(m_kind == Kind::Content);
+
+ // Media elements belonging to Content media sessions have mutually-exclusive playback.
+ ASSERT(m_activeParticipatingElements.size() <= 1);
+
+ if (m_activeParticipatingElements.isEmpty())
+ return;
+
+ HTMLMediaElement* element = *m_activeParticipatingElements.begin();
+ m_document.updateIsPlayingMedia(element->elementID());
+}
+
+}
+
+#endif /* ENABLE(MEDIA_SESSION) */