Implement Page::isPlayingAudio().
authoradachan@apple.com <adachan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 Oct 2014 18:22:27 +0000 (18:22 +0000)
committeradachan@apple.com <adachan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 6 Oct 2014 18:22:27 +0000 (18:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=137218

Reviewed by Eric Carlson.

- Add a hasMediaCharacteristics() method to MediaSession. There are three characteristics:
audible, visual, and legible. MediaSession gets the media characteristics information from
its MediaSessionClient.
- Add a mediaStateDidChange() method to MediaSessionClient that MediaSession can call when its
state changes.
- Each Document keeps a set of MediaSessions it contains. When that set changes, or when a MediaSession
changes state, or when its characteristics change, Document::updateIsPlayingAudio() is called. That method
iterates through all its MediaSessions to check if the overall isPlayingAudio state has changed in the Document.
- Each Page caches its overall isPlayingAudio state. Whenever a Document's isPlayingAudio state changes,
it calls Page::updateIsPlayingAudio() which iterates through its frames' documents to calculate its
overall isPlayingAudio state.

No new tests, no behavior change.

* dom/Document.cpp:
(WebCore::Document::Document):
Initialize m_isPlayingAudio.
(WebCore::Document::registerMediaSession):
Add the MediaSession to m_mediaSessions. Call updateIsPlayingAudio().
(WebCore::Document::unregisterMediaSession):
Remove the MediaSession from m_mediaSessions. Call updateIsPlayingAudio().
(WebCore::Document::updateIsPlayingAudio):
Go through all the MediaSessions in this Document and check if any has the Audible characteristic
and is playing. If the overall isPlayingAudio state changes, call Page::updateIsPlayingAudio().
* dom/Document.h:
(WebCore::Document::isPlayingAudio):

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::registerWithDocument):
Call Document::registerMediaSession().
(WebCore::HTMLMediaElement::unregisterWithDocument):
Call Document::unregisterMediaSession().
(WebCore::HTMLMediaElement::mediaPlayerCharacteristicChanged):
Call Document::updateIsPlayingAudio().
(WebCore::HTMLMediaElement::stop):
Call MediaSession::clientWillPausePlayback() so the MediaSession's state can be set back to Paused.
(WebCore::HTMLMediaElement::hasMediaCharacteristics):
Call hasAudio() to check if the HTMLMediaElement has the Audible characteristic. Call hasVideo()
to check if it has the Visual characteristic. Call hasClosedCaptions() to check if it has the
Legible characteristic.
(WebCore::HTMLMediaElement::mediaStateDidChange):
Call Document::updateIsPlayingAudio().
* html/HTMLMediaElement.h:

* page/Page.cpp:
(WebCore::Page::Page):
Initialize m_isPlayingAudio.
(WebCore::Page::updateIsPlayingAudio):
Iterate through all its frames' documents to check if any has audio playing. If the overall isPlayingAudio
state has changed, update m_isPlayingAudio.
* page/Page.h:
(WebCore::Page::isPlayingAudio):

* platform/audio/MediaSession.cpp:
(WebCore::MediaSession::setState):
If the MediaSession's state has changed, call Document::updateIsPlayingAudio().
(WebCore::MediaSession::hasMediaCharacteristics):
Call MediaSessionClient::hasMediaCharacteristics().
* platform/audio/MediaSession.h:
(WebCore::MediaSessionClient::mediaStateDidChange):

* platform/audio/mac/AudioDestinationMac.h:
AudioDestination only has the Audible characteristic.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@174353 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebCore/platform/audio/MediaSession.cpp
Source/WebCore/platform/audio/MediaSession.h
Source/WebCore/platform/audio/mac/AudioDestinationMac.h

index fee859b..b01c46c 100644 (file)
@@ -1,3 +1,74 @@
+2014-10-06  Ada Chan  <adachan@apple.com>
+
+        Implement Page::isPlayingAudio().
+        https://bugs.webkit.org/show_bug.cgi?id=137218
+
+        Reviewed by Eric Carlson.
+
+        - Add a hasMediaCharacteristics() method to MediaSession. There are three characteristics:
+        audible, visual, and legible. MediaSession gets the media characteristics information from
+        its MediaSessionClient.
+        - Add a mediaStateDidChange() method to MediaSessionClient that MediaSession can call when its
+        state changes.
+        - Each Document keeps a set of MediaSessions it contains. When that set changes, or when a MediaSession
+        changes state, or when its characteristics change, Document::updateIsPlayingAudio() is called. That method
+        iterates through all its MediaSessions to check if the overall isPlayingAudio state has changed in the Document.
+        - Each Page caches its overall isPlayingAudio state. Whenever a Document's isPlayingAudio state changes,
+        it calls Page::updateIsPlayingAudio() which iterates through its frames' documents to calculate its
+        overall isPlayingAudio state.
+
+        No new tests, no behavior change.
+
+        * dom/Document.cpp:
+        (WebCore::Document::Document):
+        Initialize m_isPlayingAudio.
+        (WebCore::Document::registerMediaSession):
+        Add the MediaSession to m_mediaSessions. Call updateIsPlayingAudio().
+        (WebCore::Document::unregisterMediaSession):
+        Remove the MediaSession from m_mediaSessions. Call updateIsPlayingAudio().
+        (WebCore::Document::updateIsPlayingAudio):
+        Go through all the MediaSessions in this Document and check if any has the Audible characteristic
+        and is playing. If the overall isPlayingAudio state changes, call Page::updateIsPlayingAudio().
+        * dom/Document.h:
+        (WebCore::Document::isPlayingAudio):
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::registerWithDocument):
+        Call Document::registerMediaSession().
+        (WebCore::HTMLMediaElement::unregisterWithDocument):
+        Call Document::unregisterMediaSession().
+        (WebCore::HTMLMediaElement::mediaPlayerCharacteristicChanged):
+        Call Document::updateIsPlayingAudio().
+        (WebCore::HTMLMediaElement::stop):
+        Call MediaSession::clientWillPausePlayback() so the MediaSession's state can be set back to Paused.
+        (WebCore::HTMLMediaElement::hasMediaCharacteristics):
+        Call hasAudio() to check if the HTMLMediaElement has the Audible characteristic. Call hasVideo()
+        to check if it has the Visual characteristic. Call hasClosedCaptions() to check if it has the
+        Legible characteristic.
+        (WebCore::HTMLMediaElement::mediaStateDidChange):
+        Call Document::updateIsPlayingAudio().
+        * html/HTMLMediaElement.h:
+
+        * page/Page.cpp:
+        (WebCore::Page::Page):
+        Initialize m_isPlayingAudio.
+        (WebCore::Page::updateIsPlayingAudio):
+        Iterate through all its frames' documents to check if any has audio playing. If the overall isPlayingAudio
+        state has changed, update m_isPlayingAudio.
+        * page/Page.h:
+        (WebCore::Page::isPlayingAudio):
+
+        * platform/audio/MediaSession.cpp:
+        (WebCore::MediaSession::setState):
+        If the MediaSession's state has changed, call Document::updateIsPlayingAudio().
+        (WebCore::MediaSession::hasMediaCharacteristics):
+        Call MediaSessionClient::hasMediaCharacteristics().
+        * platform/audio/MediaSession.h:
+        (WebCore::MediaSessionClient::mediaStateDidChange):
+
+        * platform/audio/mac/AudioDestinationMac.h:
+        AudioDestination only has the Audible characteristic.
+
 2014-10-06  Christophe Dumez  <cdumez@apple.com>
 
         Use is<>() / downcast<>() for ScrollingTree subclasses
index 38bd73f..17b3855 100644 (file)
@@ -99,6 +99,7 @@
 #include "MediaCanStartListener.h"
 #include "MediaQueryList.h"
 #include "MediaQueryMatcher.h"
+#include "MediaSession.h"
 #include "MouseEventWithHitTestResults.h"
 #include "NameNodeList.h"
 #include "NestingLevelIncrementer.h"
@@ -519,6 +520,7 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsig
     , m_renderTreeBeingDestroyed(false)
     , m_hasPreparedForDestruction(false)
     , m_hasStyleWithViewportUnits(false)
+    , m_isPlayingAudio(false)
 {
     allDocuments().add(this);
 
@@ -3274,6 +3276,37 @@ void Document::updateViewportUnitsOnResize()
     }
 }
 
+void Document::registerMediaSession(MediaSession& mediaSession)
+{
+    m_mediaSessions.add(&mediaSession);
+    updateIsPlayingAudio();
+}
+
+void Document::unregisterMediaSession(MediaSession& mediaSession)
+{
+    m_mediaSessions.remove(&mediaSession);
+    updateIsPlayingAudio();
+}
+
+void Document::updateIsPlayingAudio()
+{
+    bool isPlayingAudio = false;
+    for (auto mediaSession : m_mediaSessions) {
+        if (mediaSession->hasMediaCharacteristics(MediaSession::MediaCharacteristicAudible) && mediaSession->state() == MediaSession::Playing) {
+            isPlayingAudio = true;
+            break;
+        }
+    }
+
+    if (isPlayingAudio == m_isPlayingAudio)
+        return;
+
+    m_isPlayingAudio = isPlayingAudio;
+
+    if (page())
+        page()->updateIsPlayingAudio();
+}
+
 void Document::styleResolverChanged(StyleResolverUpdateFlag updateFlag)
 {
     if (m_optimizedStyleSheetUpdateTimer.isActive())
index 156afd0..6bdc062 100644 (file)
@@ -123,6 +123,7 @@ class Locale;
 class MediaCanStartListener;
 class MediaQueryList;
 class MediaQueryMatcher;
+class MediaSession;
 class MouseEventWithHitTestResults;
 class NamedFlowCollection;
 class NodeFilter;
@@ -1288,6 +1289,11 @@ public:
     bool hasStyleWithViewportUnits() const { return m_hasStyleWithViewportUnits; }
     void updateViewportUnitsOnResize();
 
+    void registerMediaSession(MediaSession&);
+    void unregisterMediaSession(MediaSession&);
+    bool isPlayingAudio() const { return m_isPlayingAudio; }
+    void updateIsPlayingAudio();
+
 protected:
     enum ConstructionFlags { Synthesized = 1, NonRenderedPlaceholder = 1 << 1 };
     Document(Frame*, const URL&, unsigned = DefaultDocumentClass, unsigned constructionFlags = 0);
@@ -1717,6 +1723,9 @@ private:
     bool m_hasPreparedForDestruction;
 
     bool m_hasStyleWithViewportUnits;
+
+    HashSet<MediaSession*> m_mediaSessions;
+    bool m_isPlayingAudio;
 };
 
 inline void Document::notifyRemovePendingSheetIfNeeded()
index 42df8af..7045d4b 100644 (file)
@@ -444,6 +444,7 @@ void HTMLMediaElement::registerWithDocument(Document& document)
         document.registerForPageScaleFactorChangedCallbacks(this);
 #endif
 
+    document.registerMediaSession(*m_mediaSession);
     addElementToDocumentMap(*this, document);
 }
 
@@ -468,6 +469,7 @@ void HTMLMediaElement::unregisterWithDocument(Document& document)
         document.unregisterForPageScaleFactorChangedCallbacks(this);
 #endif
 
+    document.unregisterMediaSession(*m_mediaSession);
     removeElementFromDocumentMap(*this, document);
 }
 
@@ -4320,6 +4322,9 @@ void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*)
         mediaControls()->reset();
     if (renderer())
         renderer()->updateFromElement();
+
+    document().updateIsPlayingAudio();
+
     endProcessingMediaPlayerCallback();
 }
 
@@ -4667,6 +4672,7 @@ void HTMLMediaElement::stop()
     // Stop the playback without generating events
     m_playing = false;
     setPausedInternal(true);
+    m_mediaSession->clientWillPausePlayback();
 
     userCancelledLoad();
 
@@ -5987,6 +5993,23 @@ bool HTMLMediaElement::overrideBackgroundPlaybackRestriction() const
     return false;
 }
 
+bool HTMLMediaElement::hasMediaCharacteristics(MediaSession::MediaCharacteristics characteristics) const
+{
+    if ((characteristics & MediaSession::MediaCharacteristicAudible) && !hasAudio())
+        return false;
+    if ((characteristics & MediaSession::MediaCharacteristicVisual) && !hasVideo())
+        return false;
+    if ((characteristics & MediaSession::MediaCharacteristicLegible) && !hasClosedCaptions())
+        return false;
+
+    return true;
+}
+
+void HTMLMediaElement::mediaStateDidChange()
+{
+    document().updateIsPlayingAudio();
+}
+
 bool HTMLMediaElement::doesHaveAttribute(const AtomicString& attribute, AtomicString* value) const
 {
     QualifiedName attributeName(nullAtom, attribute, nullAtom);
index d9a68d3..fcf1fca 100644 (file)
@@ -701,6 +701,8 @@ private:
     virtual bool canReceiveRemoteControlCommands() const override { return true; }
     virtual void didReceiveRemoteControlCommand(MediaSession::RemoteControlCommandType) override;
     virtual bool overrideBackgroundPlaybackRestriction() const override;
+    virtual bool hasMediaCharacteristics(MediaSession::MediaCharacteristics) const override;
+    virtual void mediaStateDidChange() override;
 
     void registerWithDocument(Document&);
     void unregisterWithDocument(Document&);
index a6a499a..112e69e 100644 (file)
@@ -204,6 +204,7 @@ Page::Page(PageClients& pageClients)
     , m_visitedLinkStore(WTF::move(pageClients.visitedLinkStore))
     , m_sessionID(SessionID::defaultSessionID())
     , m_isClosing(false)
+    , m_isPlayingAudio(false)
 {
     ASSERT(m_editorClient);
     
@@ -1192,6 +1193,25 @@ void Page::enableLegacyPrivateBrowsing(bool privateBrowsingEnabled)
     setSessionID(privateBrowsingEnabled ? SessionID::legacyPrivateSessionID() : SessionID::defaultSessionID());
 }
 
+void Page::updateIsPlayingAudio()
+{
+    bool isPlayingAudio = false;
+    for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        if (frame->document()->isPlayingAudio()) {
+            isPlayingAudio = true;
+            break;
+        }
+    }
+
+    if (isPlayingAudio == m_isPlayingAudio)
+        return;
+
+    m_isPlayingAudio = isPlayingAudio;
+
+    // FIXME: Notify the ChromeClient that the isPlayingAudio state has changed.
+    // https://bugs.webkit.org/show_bug.cgi?id=137220
+}
+
 #if !ASSERT_DISABLED
 void Page::checkSubframeCountConsistency() const
 {
index 2ce52b9..c927e21 100644 (file)
@@ -436,6 +436,9 @@ public:
     WEBCORE_EXPORT void enableLegacyPrivateBrowsing(bool privateBrowsingEnabled);
     bool usesEphemeralSession() const { return m_sessionID.isEphemeral(); }
 
+    bool isPlayingAudio() const { return m_isPlayingAudio; }
+    void updateIsPlayingAudio();
+
 private:
     WEBCORE_EXPORT void initGroup();
 
@@ -595,6 +598,8 @@ private:
     SessionID m_sessionID;
 
     bool m_isClosing;
+
+    bool m_isPlayingAudio;
 };
 
 inline PageGroup& Page::group()
index 5476c4e..9df9604 100644 (file)
@@ -76,7 +76,13 @@ MediaSession::~MediaSession()
 void MediaSession::setState(State state)
 {
     LOG(Media, "MediaSession::setState(%p) - %s", this, stateName(state));
+
+    if (m_state == state)
+        return;
+
     m_state = state;
+
+    client().mediaStateDidChange();
 }
 
 void MediaSession::beginInterruption(InterruptionType type)
@@ -107,6 +113,11 @@ void MediaSession::endInterruption(EndInterruptionFlags flags)
     }
 }
 
+bool MediaSession::hasMediaCharacteristics(MediaSession::MediaCharacteristics mediaCharacteristics) const
+{
+    return client().hasMediaCharacteristics(mediaCharacteristics);
+}
+
 bool MediaSession::clientWillBeginPlayback()
 {
     setState(Playing);
index 732c36f..41ac757 100644 (file)
@@ -71,6 +71,14 @@ public:
     void beginInterruption(InterruptionType);
     void endInterruption(EndInterruptionFlags);
 
+    enum MediaCharacteristicFlags {
+        MediaCharacteristicAudible = 1,
+        MediaCharacteristicVisual = 1 << 1,
+        MediaCharacteristicLegible = 1 << 2,
+    };
+    typedef unsigned MediaCharacteristics;
+    bool hasMediaCharacteristics(MediaCharacteristics) const;
+
     void applicationWillEnterForeground() const;
     void applicationWillEnterBackground() const;
 
@@ -136,6 +144,9 @@ public:
 
     virtual bool overrideBackgroundPlaybackRestriction() const = 0;
 
+    virtual bool hasMediaCharacteristics(MediaSession::MediaCharacteristics) const = 0;
+    virtual void mediaStateDidChange() { }
+
 protected:
     virtual ~MediaSessionClient() { }
 };
index 1fcf973..2b028f2 100644 (file)
@@ -57,6 +57,7 @@ private:
     virtual bool canReceiveRemoteControlCommands() const override { return false; }
     virtual void didReceiveRemoteControlCommand(MediaSession::RemoteControlCommandType) override { }
     virtual bool overrideBackgroundPlaybackRestriction() const override { return false; }
+    virtual bool hasMediaCharacteristics(MediaSession::MediaCharacteristics characteristics) const override { return characteristics == MediaSession::MediaCharacteristicAudible; }
 
     virtual void start() override;
     virtual void stop() override;