- Revision
- 206315
- Author
- [email protected]
- Date
- 2016-09-23 11:45:53 -0700 (Fri, 23 Sep 2016)
Log Message
MediaSessionManagerMac::nowPlayingEligibleSession() needs to honor the main content heuristic
https://bugs.webkit.org/show_bug.cgi?id=162480
<rdar://problem/28430615>
Reviewed by Jer Noble.
Changes the implementation of nowPlayingEligibleSession to use bestMediaElementForShowingPlaybackControlsManager
and also early return nullptr if the current tab the web process is hosted in is the active tab, and the window
it is hosted in is the main window. This information is derived from the viewState flags in the Page of each
tab -- whenever the (visible && active) state changes, the Page tells the global media session manager to update
its Now Playing info. Then, when each MediaElementSession tries to determine whether it can show playback
controls for the purposes of Now Playing, each session consults its page's visible and active state. If a page
is both visible and active, no Now Playing controls are allowed for that media session.
Also adds some slight adjustments to MediaSessionManagerMac::updateNowPlayingInfo, so we reset the title, rate
and duration of the current active session when clearing out the now playing info. Likewise, when vending an
active video, if the video information matches that of the current session, we mark m_nowPlayingActive anyways.
These tweaks prevent us from getting in a bad state when switching between a tab with media and one without.
Unit tests to come in a future patch.
* html/HTMLMediaElement.cpp:
(WebCore::mediaElementSessionInfoForSession):
(WebCore::mediaSessionMayBeConfusedWithMainContent):
(WebCore::HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager):
(WebCore::HTMLMediaElement::updatePlaybackControlsManager):
(WebCore::bestMediaSessionForShowingPlaybackControlsManager): Deleted.
* html/HTMLMediaElement.h:
* html/MediaElementSession.cpp:
(WebCore::MediaElementSession::canShowControlsManager):
(WebCore::MediaElementSession::pageAllowsNowPlayingControls):
* html/MediaElementSession.h:
* page/Page.cpp:
(WebCore::Page::setViewState):
(WebCore::Page::isVisibleAndActive):
* page/Page.h:
* platform/audio/PlatformMediaSessionManager.cpp:
(WebCore::PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary):
* platform/audio/PlatformMediaSessionManager.h:
* platform/audio/mac/MediaSessionManagerMac.mm:
(WebCore::PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary):
(WebCore::MediaSessionManagerMac::nowPlayingEligibleSession):
(WebCore::MediaSessionManagerMac::updateNowPlayingInfo):
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (206314 => 206315)
--- trunk/Source/WebCore/ChangeLog 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/ChangeLog 2016-09-23 18:45:53 UTC (rev 206315)
@@ -1,3 +1,49 @@
+2016-09-23 Wenson Hsieh <[email protected]>
+
+ MediaSessionManagerMac::nowPlayingEligibleSession() needs to honor the main content heuristic
+ https://bugs.webkit.org/show_bug.cgi?id=162480
+ <rdar://problem/28430615>
+
+ Reviewed by Jer Noble.
+
+ Changes the implementation of nowPlayingEligibleSession to use bestMediaElementForShowingPlaybackControlsManager
+ and also early return nullptr if the current tab the web process is hosted in is the active tab, and the window
+ it is hosted in is the main window. This information is derived from the viewState flags in the Page of each
+ tab -- whenever the (visible && active) state changes, the Page tells the global media session manager to update
+ its Now Playing info. Then, when each MediaElementSession tries to determine whether it can show playback
+ controls for the purposes of Now Playing, each session consults its page's visible and active state. If a page
+ is both visible and active, no Now Playing controls are allowed for that media session.
+
+ Also adds some slight adjustments to MediaSessionManagerMac::updateNowPlayingInfo, so we reset the title, rate
+ and duration of the current active session when clearing out the now playing info. Likewise, when vending an
+ active video, if the video information matches that of the current session, we mark m_nowPlayingActive anyways.
+ These tweaks prevent us from getting in a bad state when switching between a tab with media and one without.
+
+ Unit tests to come in a future patch.
+
+ * html/HTMLMediaElement.cpp:
+ (WebCore::mediaElementSessionInfoForSession):
+ (WebCore::mediaSessionMayBeConfusedWithMainContent):
+ (WebCore::HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager):
+ (WebCore::HTMLMediaElement::updatePlaybackControlsManager):
+ (WebCore::bestMediaSessionForShowingPlaybackControlsManager): Deleted.
+ * html/HTMLMediaElement.h:
+ * html/MediaElementSession.cpp:
+ (WebCore::MediaElementSession::canShowControlsManager):
+ (WebCore::MediaElementSession::pageAllowsNowPlayingControls):
+ * html/MediaElementSession.h:
+ * page/Page.cpp:
+ (WebCore::Page::setViewState):
+ (WebCore::Page::isVisibleAndActive):
+ * page/Page.h:
+ * platform/audio/PlatformMediaSessionManager.cpp:
+ (WebCore::PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary):
+ * platform/audio/PlatformMediaSessionManager.h:
+ * platform/audio/mac/MediaSessionManagerMac.mm:
+ (WebCore::PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary):
+ (WebCore::MediaSessionManagerMac::nowPlayingEligibleSession):
+ (WebCore::MediaSessionManagerMac::updateNowPlayingInfo):
+
2016-09-23 Commit Queue <[email protected]>
Unreviewed, rolling out r206311.
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (206314 => 206315)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2016-09-23 18:45:53 UTC (rev 206315)
@@ -358,13 +358,13 @@
bool isPlayingAudio : 1;
};
-static MediaElementSessionInfo mediaElementSessionInfoForSession(const MediaElementSession& session)
+static MediaElementSessionInfo mediaElementSessionInfoForSession(const MediaElementSession& session, MediaElementSession::PlaybackControlsPurpose purpose)
{
const HTMLMediaElement& element = session.element();
return {
&session,
session.mostRecentUserInteractionTime(),
- session.canShowControlsManager(),
+ session.canShowControlsManager(purpose),
element.isFullscreen() || element.isVisibleInViewport(),
session.isLargeEnoughForMainContent(MediaSessionMainContentPurpose::MediaControls),
element.isPlaying() && element.hasAudio() && !element.muted()
@@ -381,8 +381,11 @@
return session.timeOfLastUserInteraction > otherSession.timeOfLastUserInteraction;
}
-static bool mediaSessionMayBeConfusedWithMainContent(const MediaElementSessionInfo& session)
+static bool mediaSessionMayBeConfusedWithMainContent(const MediaElementSessionInfo& session, MediaElementSession::PlaybackControlsPurpose purpose)
{
+ if (purpose == MediaElementSession::PlaybackControlsPurpose::NowPlaying)
+ return session.isPlayingAudio;
+
if (!session.isVisibleInViewportOrFullscreen)
return false;
@@ -394,33 +397,6 @@
return true;
}
-static const MediaElementSession* bestMediaSessionForShowingPlaybackControlsManager()
-{
- auto allSessions = PlatformMediaSessionManager::sharedManager().currentSessionsMatching([] (const PlatformMediaSession& session) {
- return is<MediaElementSession>(session);
- });
-
- Vector<MediaElementSessionInfo> candidateSessions;
- bool atLeastOneNonCandidateMayBeConfusedForMainContent = false;
- for (auto& session : allSessions) {
- auto mediaElementSessionInfo = mediaElementSessionInfoForSession(downcast<MediaElementSession>(*session));
- if (mediaElementSessionInfo.canShowControlsManager)
- candidateSessions.append(mediaElementSessionInfo);
- else if (mediaSessionMayBeConfusedWithMainContent(mediaElementSessionInfo))
- atLeastOneNonCandidateMayBeConfusedForMainContent = true;
- }
-
- if (!candidateSessions.size())
- return nullptr;
-
- std::sort(candidateSessions.begin(), candidateSessions.end(), preferMediaControlsForCandidateSessionOverOtherCandidateSession);
- auto strongestSessionCandidate = candidateSessions.first();
- if (!strongestSessionCandidate.isVisibleInViewportOrFullscreen && !strongestSessionCandidate.isPlayingAudio && atLeastOneNonCandidateMayBeConfusedForMainContent)
- return nullptr;
-
- return strongestSessionCandidate.session;
-}
-
HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document, bool createdByParser)
: HTMLElement(tagName, document)
, ActiveDOMObject(&document)
@@ -643,6 +619,33 @@
updatePlaybackControlsManager();
}
+HTMLMediaElement* HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose purpose)
+{
+ auto allSessions = PlatformMediaSessionManager::sharedManager().currentSessionsMatching([] (const PlatformMediaSession& session) {
+ return is<MediaElementSession>(session);
+ });
+
+ Vector<MediaElementSessionInfo> candidateSessions;
+ bool atLeastOneNonCandidateMayBeConfusedForMainContent = false;
+ for (auto& session : allSessions) {
+ auto mediaElementSessionInfo = mediaElementSessionInfoForSession(downcast<MediaElementSession>(*session), purpose);
+ if (mediaElementSessionInfo.canShowControlsManager)
+ candidateSessions.append(mediaElementSessionInfo);
+ else if (mediaSessionMayBeConfusedWithMainContent(mediaElementSessionInfo, purpose))
+ atLeastOneNonCandidateMayBeConfusedForMainContent = true;
+ }
+
+ if (!candidateSessions.size())
+ return nullptr;
+
+ std::sort(candidateSessions.begin(), candidateSessions.end(), preferMediaControlsForCandidateSessionOverOtherCandidateSession);
+ auto strongestSessionCandidate = candidateSessions.first();
+ if (!strongestSessionCandidate.isVisibleInViewportOrFullscreen && !strongestSessionCandidate.isPlayingAudio && atLeastOneNonCandidateMayBeConfusedForMainContent)
+ return nullptr;
+
+ return &strongestSessionCandidate.session->element();
+}
+
void HTMLMediaElement::registerWithDocument(Document& document)
{
m_mediaSession->registerWithDocument(document);
@@ -7326,8 +7329,8 @@
return;
// FIXME: Ensure that the renderer here should be up to date.
- if (auto bestMediaSession = bestMediaSessionForShowingPlaybackControlsManager())
- page->chrome().client().setUpPlaybackControlsManager(bestMediaSession->element());
+ if (auto bestMediaElement = bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose::ControlsManager))
+ page->chrome().client().setUpPlaybackControlsManager(*bestMediaElement);
else
page->chrome().client().clearPlaybackControlsManager();
}
Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (206314 => 206315)
--- trunk/Source/WebCore/html/HTMLMediaElement.h 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h 2016-09-23 18:45:53 UTC (rev 206315)
@@ -115,6 +115,8 @@
static HashSet<HTMLMediaElement*>& allMediaElements();
+ static HTMLMediaElement* bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose);
+
void rewind(double timeDelta);
WEBCORE_EXPORT void returnToRealtime() override;
Modified: trunk/Source/WebCore/html/MediaElementSession.cpp (206314 => 206315)
--- trunk/Source/WebCore/html/MediaElementSession.cpp 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/html/MediaElementSession.cpp 2016-09-23 18:45:53 UTC (rev 206315)
@@ -216,8 +216,13 @@
return true;
}
-bool MediaElementSession::canShowControlsManager() const
+bool MediaElementSession::canShowControlsManager(PlaybackControlsPurpose purpose) const
{
+ if (purpose == PlaybackControlsPurpose::NowPlaying && !pageAllowsNowPlayingControls()) {
+ LOG(Media, "MediaElementSession::canShowControlsManager - returning FALSE: Now playing not allowed in foreground tab");
+ return false;
+ }
+
if (m_element.isFullscreen()) {
LOG(Media, "MediaElementSession::canShowControlsManager - returning TRUE: Is fullscreen");
return true;
@@ -228,7 +233,8 @@
return false;
}
- if (!m_element.hasAudio() && !m_element.hasEverHadAudio()) {
+ bool meetsAudioTrackRequirements = m_element.hasAudio() || (purpose == PlaybackControlsPurpose::ControlsManager && m_element.hasEverHadAudio());
+ if (!meetsAudioTrackRequirements) {
LOG(Media, "MediaElementSession::canShowControlsManager - returning FALSE: No audio");
return false;
}
@@ -274,7 +280,7 @@
return false;
}
- if (!m_element.hasVideo() && !m_element.hasEverHadVideo()) {
+ if (purpose == PlaybackControlsPurpose::ControlsManager && !m_element.hasVideo() && !m_element.hasEverHadVideo()) {
LOG(Media, "MediaElementSession::canShowControlsManager - returning FALSE: No video");
return false;
}
@@ -722,6 +728,12 @@
return m_isMainContent;
}
+bool MediaElementSession::pageAllowsNowPlayingControls() const
+{
+ auto page = m_element.document().page();
+ return page && !page->isVisibleAndActive();
}
+}
+
#endif // ENABLE(VIDEO)
Modified: trunk/Source/WebCore/html/MediaElementSession.h (206314 => 206315)
--- trunk/Source/WebCore/html/MediaElementSession.h 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/html/MediaElementSession.h 2016-09-23 18:45:53 UTC (rev 206315)
@@ -118,7 +118,9 @@
bool wantsToObserveViewportVisibilityForMediaControls() const;
bool wantsToObserveViewportVisibilityForAutoplay() const;
- bool canShowControlsManager() const;
+
+ enum class PlaybackControlsPurpose { ControlsManager, NowPlaying };
+ bool canShowControlsManager(PlaybackControlsPurpose) const;
bool isLargeEnoughForMainContent(MediaSessionMainContentPurpose) const;
double mostRecentUserInteractionTime() const;
@@ -138,6 +140,8 @@
bool updateIsMainContent() const;
void mainContentCheckTimerFired();
+ bool pageAllowsNowPlayingControls() const;
+
HTMLMediaElement& m_element;
BehaviorRestrictions m_restrictions;
Modified: trunk/Source/WebCore/page/Page.cpp (206314 => 206315)
--- trunk/Source/WebCore/page/Page.cpp 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/page/Page.cpp 2016-09-23 18:45:53 UTC (rev 206315)
@@ -67,6 +67,7 @@
#include "PageGroup.h"
#include "PageOverlayController.h"
#include "PageThrottler.h"
+#include "PlatformMediaSessionManager.h"
#include "PlugInClient.h"
#include "PluginData.h"
#include "PluginInfoProvider.h"
@@ -1449,7 +1450,9 @@
ViewState::Flags oldViewState = m_viewState;
+ bool wasVisibleAndActive = isVisibleAndActive();
m_viewState = viewState;
+
m_focusController->setViewState(viewState);
if (changed & ViewState::IsVisible)
@@ -1464,8 +1467,16 @@
for (auto* observer : m_viewStateChangeObservers)
observer->viewStateDidChange(oldViewState, m_viewState);
+
+ if (wasVisibleAndActive != isVisibleAndActive())
+ PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary();
}
+bool Page::isVisibleAndActive() const
+{
+ return (m_viewState & ViewState::IsVisible) && (m_viewState & ViewState::WindowIsActive);
+}
+
void Page::setPageActivityState(PageActivityState::Flags activityState)
{
chrome().client().setPageActivityState(activityState);
Modified: trunk/Source/WebCore/page/Page.h (206314 => 206315)
--- trunk/Source/WebCore/page/Page.h 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/page/Page.h 2016-09-23 18:45:53 UTC (rev 206315)
@@ -335,6 +335,7 @@
// Notifications when the Page starts and stops being presented via a native window.
WEBCORE_EXPORT void setViewState(ViewState::Flags);
+ bool isVisibleAndActive() const;
void setPageActivityState(PageActivityState::Flags);
WEBCORE_EXPORT void setIsVisible(bool);
WEBCORE_EXPORT void setIsPrerender();
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp (206314 => 206315)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp 2016-09-23 18:45:53 UTC (rev 206315)
@@ -36,7 +36,13 @@
namespace WebCore {
-#if !PLATFORM(IOS) && !PLATFORM(MAC)
+#if !PLATFORM(MAC)
+
+void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
+{
+}
+
+#if !PLATFORM(IOS)
static PlatformMediaSessionManager* platformMediaSessionManager = nullptr;
PlatformMediaSessionManager& PlatformMediaSessionManager::sharedManager()
@@ -50,8 +56,10 @@
{
return platformMediaSessionManager;
}
-#endif
+#endif // !PLATFORM(IOS)
+#endif // !PLATFORM(MAC)
+
PlatformMediaSessionManager::PlatformMediaSessionManager()
: m_systemSleepListener(SystemSleepListener::create(*this))
{
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h (206314 => 206315)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h 2016-09-23 18:45:53 UTC (rev 206315)
@@ -46,6 +46,9 @@
public:
WEBCORE_EXPORT static PlatformMediaSessionManager* sharedManagerIfExists();
WEBCORE_EXPORT static PlatformMediaSessionManager& sharedManager();
+
+ static void updateNowPlayingInfoIfNecessary();
+
virtual ~PlatformMediaSessionManager() { }
bool has(PlatformMediaSession::MediaType) const;
Modified: trunk/Source/WebCore/platform/audio/mac/MediaSessionManagerMac.mm (206314 => 206315)
--- trunk/Source/WebCore/platform/audio/mac/MediaSessionManagerMac.mm 2016-09-23 18:09:44 UTC (rev 206314)
+++ trunk/Source/WebCore/platform/audio/mac/MediaSessionManagerMac.mm 2016-09-23 18:45:53 UTC (rev 206315)
@@ -28,6 +28,7 @@
#if PLATFORM(MAC)
+#import "HTMLMediaElement.h"
#import "Logging.h"
#import "MediaPlayer.h"
#import "PlatformMediaSession.h"
@@ -52,6 +53,12 @@
return platformMediaSessionManager;
}
+void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
+{
+ if (auto existingManager = (MediaSessionManagerMac *)PlatformMediaSessionManager::sharedManagerIfExists())
+ existingManager->updateNowPlayingInfo();
+}
+
MediaSessionManagerMac::MediaSessionManagerMac()
: PlatformMediaSessionManager()
{
@@ -94,15 +101,9 @@
PlatformMediaSession* MediaSessionManagerMac::nowPlayingEligibleSession()
{
- for (auto session : sessions()) {
- PlatformMediaSession::MediaType type = session->mediaType();
- if (type != PlatformMediaSession::Video && type != PlatformMediaSession::Audio)
- continue;
+ if (auto element = HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose::NowPlaying))
+ return &element->mediaSession();
- if (session->characteristics() & PlatformMediaSession::HasAudio)
- return session;
- }
-
return nullptr;
}
@@ -122,6 +123,9 @@
LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - clearing now playing info");
MRMediaRemoteSetNowPlayingInfo(nullptr);
m_nowPlayingActive = false;
+ m_reportedTitle = "";
+ m_reportedRate = 0;
+ m_reportedDuration = 0;
MRMediaRemoteSetNowPlayingApplicationPlaybackStateForOrigin(MRMediaRemoteGetLocalOrigin(), kMRPlaybackStateStopped, dispatch_get_main_queue(), ^(MRMediaRemoteError error) {
#if LOG_DISABLED
UNUSED_PARAM(error);
@@ -143,6 +147,7 @@
double duration = currentSession->duration();
double rate = currentSession->state() == PlatformMediaSession::Playing ? 1 : 0;
if (m_reportedTitle == title && m_reportedRate == rate && m_reportedDuration == duration) {
+ m_nowPlayingActive = true;
LOG(Media, "MediaSessionManagerMac::updateNowPlayingInfo - nothing new to show");
return;
}