Limit use of display sleep assertion when <video> element is off-screen.
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 21 Oct 2013 21:20:55 +0000 (21:20 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 21 Oct 2013 21:20:55 +0000 (21:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=123041

Reviewed by Darin Adler.

Use page visibility changes to suspend and resume the use of sleep assertions in
HTMLMediaElement.

Page will propogate the page visibility change notifications to its Documents, which
will further propogate those notifications to registered elements.  Upon receiving
these notifications, HTMLMediaElement will release or take a DisplaySleepDisabler
token if necessary.

Also, rename HTMLMediaElement's updateDisableSleep() to updateSleepDisabling() and wrap
the implementation in a PLATFORM(MAC) guard rather than at each call site.

* dom/Document.cpp:
(WebCore::Document::registerForVisibilityStateCallbacks): Added registration method.
(WebCore::Document::unregisterForVisibilityStateCallbacks): Added unregistration method.
(WebCore::Document::visibilityStateChanged): Call all registered clients.
* dom/Document.h:
* dom/Element.h:
(WebCore::Element::visibilityStateChanged): Added default virtual method to be overridden
    by subclasses.
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement): Register for the notification, and check the
    current status of Document::hidden().
(WebCore::HTMLMediaElement::~HTMLMediaElement): Unregister for the notification.
(WebCore::HTMLMediaElement::visibilityStateChanged): Set m_displaySleepDisablingSuspended
    and call updateSleepDisabling().
(WebCore::HTMLMediaElement::shouldDisableSleep): Add a check for m_displaySleepDisablingSuspended.
* html/HTMLMediaElement.h:
* page/Page.cpp:
(WebCore::Page::setVisibilityState): Pass to every child document.

Rename updateDisableSleep() -> updateSleepDisabling():
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement):
(WebCore::HTMLMediaElement::~HTMLMediaElement):
(WebCore::HTMLMediaElement::parseAttribute):
(WebCore::HTMLMediaElement::mediaPlayerRateChanged):
(WebCore::HTMLMediaElement::clearMediaPlayer):
(WebCore::HTMLMediaElement::stop):

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

Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/Element.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/page/Page.cpp

index ff55bc9..d759e48 100644 (file)
@@ -1,3 +1,49 @@
+2013-10-21  Jer Noble  <jer.noble@apple.com>
+
+        Limit use of display sleep assertion when <video> element is off-screen.
+        https://bugs.webkit.org/show_bug.cgi?id=123041
+
+        Reviewed by Darin Adler.
+
+        Use page visibility changes to suspend and resume the use of sleep assertions in
+        HTMLMediaElement.
+
+        Page will propogate the page visibility change notifications to its Documents, which
+        will further propogate those notifications to registered elements.  Upon receiving
+        these notifications, HTMLMediaElement will release or take a DisplaySleepDisabler
+        token if necessary.
+
+        Also, rename HTMLMediaElement's updateDisableSleep() to updateSleepDisabling() and wrap
+        the implementation in a PLATFORM(MAC) guard rather than at each call site.
+
+        * dom/Document.cpp:
+        (WebCore::Document::registerForVisibilityStateCallbacks): Added registration method.
+        (WebCore::Document::unregisterForVisibilityStateCallbacks): Added unregistration method.
+        (WebCore::Document::visibilityStateChanged): Call all registered clients.
+        * dom/Document.h:
+        * dom/Element.h:
+        (WebCore::Element::visibilityStateChanged): Added default virtual method to be overridden
+            by subclasses.
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement): Register for the notification, and check the
+            current status of Document::hidden().
+        (WebCore::HTMLMediaElement::~HTMLMediaElement): Unregister for the notification.
+        (WebCore::HTMLMediaElement::visibilityStateChanged): Set m_displaySleepDisablingSuspended
+            and call updateSleepDisabling().
+        (WebCore::HTMLMediaElement::shouldDisableSleep): Add a check for m_displaySleepDisablingSuspended.
+        * html/HTMLMediaElement.h:
+        * page/Page.cpp:
+        (WebCore::Page::setVisibilityState): Pass to every child document.
+
+        Rename updateDisableSleep() -> updateSleepDisabling():
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement):
+        (WebCore::HTMLMediaElement::~HTMLMediaElement):
+        (WebCore::HTMLMediaElement::parseAttribute):
+        (WebCore::HTMLMediaElement::mediaPlayerRateChanged):
+        (WebCore::HTMLMediaElement::clearMediaPlayer):
+        (WebCore::HTMLMediaElement::stop):
+
 2013-10-21  Thiago de Barros Lacerda  <thiago.lacerda@openbossa.org>
 
         MediaStreamTrack now tracks its own state
index 5dd381d..b7f6f27 100644 (file)
@@ -1528,6 +1528,23 @@ void Document::removeTitle(Element* titleElement)
 }
 
 #if ENABLE(PAGE_VISIBILITY_API)
+void Document::registerForVisibilityStateChangedCallbacks(Element* element)
+{
+    m_visibilityStateCallbackElements.add(element);
+}
+
+void Document::unregisterForVisibilityStateChangedCallbacks(Element* element)
+{
+    m_visibilityStateCallbackElements.remove(element);
+}
+
+void Document::visibilityStateChanged()
+{
+    dispatchEvent(Event::create(eventNames().visibilitychangeEvent, false, false));
+    for (auto it = m_visibilityStateCallbackElements.begin(); it != m_visibilityStateCallbackElements.end(); ++it)
+        (*it)->visibilityStateChanged();
+}
+
 PageVisibilityState Document::pageVisibilityState() const
 {
     // The visibility of the document is inherited from the visibility of the
index 1f95373..b858778 100644 (file)
@@ -417,6 +417,7 @@ public:
     virtual URL baseURI() const OVERRIDE;
 
 #if ENABLE(PAGE_VISIBILITY_API)
+    void visibilityStateChanged();
     String visibilityState() const;
     bool hidden() const;
 #endif
@@ -967,6 +968,11 @@ public:
     void captionPreferencesChanged();
 #endif
 
+#if ENABLE(PAGE_VISIBILITY_API)
+    void registerForVisibilityStateChangedCallbacks(Element*);
+    void unregisterForVisibilityStateChangedCallbacks(Element*);
+#endif
+
     void setShouldCreateRenderers(bool);
     bool shouldCreateRenderers();
 
@@ -1439,6 +1445,10 @@ private:
     HashSet<Element*> m_captionPreferencesChangedElements;
 #endif
 
+#if ENABLE(PAGE_VISIBILITY_API)
+    HashSet<Element*> m_visibilityStateCallbackElements;
+#endif
+
     HashMap<StringImpl*, Element*, CaseFoldingHash> m_elementsByAccessKey;
     bool m_accessKeyMapValid;
 
index 1be0037..c91804f 100644 (file)
@@ -418,6 +418,11 @@ public:
     virtual void didBecomeFullscreenElement() { }
     virtual void willStopBeingFullscreenElement() { }
 
+#if ENABLE(PAGE_VISIBILITY_API)
+    // Use Document::registerForVisibilityStateChangedCallbacks() to subscribe to this.
+    virtual void visibilityStateChanged() { }
+#endif
+
 #if ENABLE(VIDEO_TRACK)
     virtual void captionPreferencesChanged() { }
 #endif
index b5e2269..7637033 100644 (file)
@@ -317,6 +317,9 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
     , m_completelyLoaded(false)
     , m_havePreparedToPlay(false)
     , m_parsingInProgress(createdByParser)
+#if ENABLE(PAGE_VISIBILITY_API)
+    , m_isDisplaySleepDisablingSuspended(document.hidden())
+#endif
 #if ENABLE(VIDEO_TRACK)
     , m_tracksAreReady(true)
     , m_haveVisibleTextTrack(false)
@@ -342,6 +345,10 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
     document.registerForMediaVolumeCallbacks(this);
     document.registerForPrivateBrowsingStateChangedCallbacks(this);
 
+#if ENABLE(PAGE_VISIBILITY_API)
+    document.registerForVisibilityStateChangedCallbacks(this);
+#endif
+
     if (document.settings() && document.settings()->mediaPlaybackRequiresUserGesture()) {
         addBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
         addBehaviorRestriction(RequireUserGestureForLoadRestriction);
@@ -367,6 +374,11 @@ HTMLMediaElement::~HTMLMediaElement()
     setShouldDelayLoadEvent(false);
     document().unregisterForMediaVolumeCallbacks(this);
     document().unregisterForPrivateBrowsingStateChangedCallbacks(this);
+
+#if ENABLE(PAGE_VISIBILITY_API)
+    document().unregisterForVisibilityStateChangedCallbacks(this);
+#endif
+
 #if ENABLE(VIDEO_TRACK)
     document().unregisterForCaptionPreferencesChangedCallbacks(this);
     if (m_audioTracks) {
@@ -459,10 +471,8 @@ void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicStr
         }
     } else if (name == controlsAttr)
         configureMediaControls();
-#if PLATFORM(MAC)
     else if (name == loopAttr)
-        updateDisableSleep();
-#endif
+        updateSleepDisabling();
     else if (name == preloadAttr) {
         if (equalIgnoringCase(value, "none"))
             m_preload = MediaPlayer::None;
@@ -3754,9 +3764,7 @@ void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
     if (m_playing)
         invalidateCachedTime();
 
-#if PLATFORM(MAC)
-    updateDisableSleep();
-#endif
+    updateSleepDisabling();
 
     endProcessingMediaPlayerCallback();
 }
@@ -4171,9 +4179,7 @@ void HTMLMediaElement::clearMediaPlayer(int flags)
         configureTextTrackDisplay();
 #endif
 
-#if PLATFORM(MAC)
-    updateDisableSleep();
-#endif
+    updateSleepDisabling();
 }
 
 bool HTMLMediaElement::canSuspend() const
@@ -4207,9 +4213,7 @@ void HTMLMediaElement::stop()
     // if the media was not fully loaded. This handles all other cases.
     m_player.clear();
 
-#if PLATFORM(MAC)
-    updateDisableSleep();
-#endif
+    updateSleepDisabling();
 }
 
 void HTMLMediaElement::suspend(ReasonForSuspension why)
@@ -4268,6 +4272,15 @@ void HTMLMediaElement::mediaVolumeDidChange()
     updateVolume();
 }
 
+#if ENABLE(PAGE_VISIBILITY_API)
+void HTMLMediaElement::visibilityStateChanged()
+{
+    LOG(Media, "HTMLMediaElement::visibilityStateChanged");
+    m_isDisplaySleepDisablingSuspended = document().hidden();
+    updateSleepDisabling();
+}
+#endif
+
 void HTMLMediaElement::defaultEventHandler(Event* event)
 {
 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
@@ -5025,17 +5038,24 @@ void HTMLMediaElement::applyMediaFragmentURI()
     }
 }
 
-#if PLATFORM(MAC)
-void HTMLMediaElement::updateDisableSleep()
+void HTMLMediaElement::updateSleepDisabling()
 {
+#if PLATFORM(MAC)
     if (!shouldDisableSleep() && m_sleepDisabler)
         m_sleepDisabler = nullptr;
     else if (shouldDisableSleep() && !m_sleepDisabler)
         m_sleepDisabler = DisplaySleepDisabler::create("com.apple.WebCore: HTMLMediaElement playback");
+#endif
 }
 
+#if PLATFORM(MAC)
 bool HTMLMediaElement::shouldDisableSleep() const
 {
+#if ENABLE(PAGE_VISIBILITY_API)
+    if (m_isDisplaySleepDisablingSuspended)
+        return false;
+#endif
+
     return m_player && !m_player->paused() && hasVideo() && hasAudio() && !loop();
 }
 #endif
index 582f035..32fab27 100644 (file)
@@ -458,6 +458,10 @@ private:
     
     virtual void mediaVolumeDidChange() OVERRIDE;
 
+#if ENABLE(PAGE_VISIBILITY_API)
+    virtual void visibilityStateChanged() OVERRIDE;
+#endif
+
     virtual void updateDisplayState() { }
     
     void setReadyState(MediaPlayer::ReadyState);
@@ -621,7 +625,7 @@ private:
     bool isAutoplaying() const { return m_autoplaying; }
 
 #if PLATFORM(MAC)
-    void updateDisableSleep();
+    void updateSleepDisabling();
     bool shouldDisableSleep() const;
 #endif
 
@@ -730,6 +734,9 @@ private:
     bool m_completelyLoaded : 1;
     bool m_havePreparedToPlay : 1;
     bool m_parsingInProgress : 1;
+#if ENABLE(PAGE_VISIBILITY_API)
+    bool m_isDisplaySleepDisablingSuspended : 1;
+#endif
 
 #if ENABLE(VIDEO_TRACK)
     bool m_tracksAreReady : 1;
index 6ca0f0f..dc6ebc0 100644 (file)
@@ -1244,7 +1244,7 @@ void Page::setVisibilityState(PageVisibilityState visibilityState, bool isInitia
             documents.append(*frame->document());
 
         for (size_t i = 0, size = documents.size(); i < size; ++i)
-            documents[i]->dispatchEvent(Event::create(eventNames().visibilitychangeEvent, false, false));
+            documents[i]->visibilityStateChanged();
     }
 #endif