Title: [187031] trunk
Revision
187031
Author
[email protected]
Date
2015-07-20 13:16:18 -0700 (Mon, 20 Jul 2015)

Log Message

Improve behavior of media elements in page cache.
<https://webkit.org/b/147020>
<rdar://problem/21712311>

Reviewed by Chris Dumez.

Source/WebCore:

Make improvements for media elements when transitioning in/out of page cache:

- Events that were scheduled when going into cache will now be delivered
  when the page is restored from cache.

- Data buffering is turned off while in the cache. This reduces the memory
  cost of cached pages with media elements on iOS (where mediaserverd would
  keep upcoming video frames in memory for cached pages.)

Test: media/restore-from-page-cache.html (amended)

* dom/GenericEventQueue.h:
* dom/GenericEventQueue.cpp:
(WebCore::GenericEventQueue::enqueueEvent):
(WebCore::GenericEventQueue::suspend):
(WebCore::GenericEventQueue::resume):

    Add a simple suspend/resume mechanism to GenericEventQueue that can
    be used to support page caching.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::stop):
(WebCore::HTMLMediaElement::suspend):
(WebCore::HTMLMediaElement::resume):
(WebCore::HTMLMediaElement::stopWithoutDestroyingMediaPlayer):

    Adapt to event queueing changes and add calls to setShouldBufferData().

* html/HTMLSourceElement.h:
* html/HTMLSourceElement.cpp:
(WebCore::HTMLSourceElement::HTMLSourceElement):
(WebCore::HTMLSourceElement::create):
(WebCore::HTMLSourceElement::activeDOMObjectName):
(WebCore::HTMLSourceElement::canSuspendForPageCache):
(WebCore::HTMLSourceElement::suspend):
(WebCore::HTMLSourceElement::resume):
(WebCore::HTMLSourceElement::stop):

    Turn HTMLSourceElement into an ActiveDOMObject so it gets all the
    appropriate page cache notifications directly. Suspend the delayed
    error event delivery timer when cached.

LayoutTests:

Add some coverage for suspend/resume of queued events on cached media elements.

* media/restore-from-page-cache-expected.txt:
* media/restore-from-page-cache.html:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (187030 => 187031)


--- trunk/LayoutTests/ChangeLog	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/LayoutTests/ChangeLog	2015-07-20 20:16:18 UTC (rev 187031)
@@ -1,3 +1,16 @@
+2015-07-20  Andreas Kling  <[email protected]>
+
+        Improve behavior of media elements in page cache.
+        <https://webkit.org/b/147020>
+        <rdar://problem/21712311>
+
+        Reviewed by Chris Dumez.
+
+        Add some coverage for suspend/resume of queued events on cached media elements.
+
+        * media/restore-from-page-cache-expected.txt:
+        * media/restore-from-page-cache.html:
+
 2015-07-19  Tim Horton  <[email protected]>
 
         Make shrink-wrapping test a ref-test instead of pixel-test

Modified: trunk/LayoutTests/media/restore-from-page-cache-expected.txt (187030 => 187031)


--- trunk/LayoutTests/media/restore-from-page-cache-expected.txt	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/LayoutTests/media/restore-from-page-cache-expected.txt	2015-07-20 20:16:18 UTC (rev 187031)
@@ -5,8 +5,12 @@
 EVENT(canplaythrough)
 EXPECTED (loadCount == '0') OK
 *** Page going into cache
+*** Changing playbackRate just before going into cache, to schedule a ratechange event.
+*** Changing volume just before going into cache, to schedule a volumechange event.
 *** Page returned from cache
 EXPECTED (loadCount == '1') OK
 EXPECTED (videoComputedStyle.width == '320px') OK
 EXPECTED (videoComputedStyle.height == '240px') OK
+*** ratechange event fired. This should happen AFTER returning from cache.
+*** volumechange event fired. This should happen AFTER returning from cache.
 

Modified: trunk/LayoutTests/media/restore-from-page-cache.html (187030 => 187031)


--- trunk/LayoutTests/media/restore-from-page-cache.html	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/LayoutTests/media/restore-from-page-cache.html	2015-07-20 20:16:18 UTC (rev 187031)
@@ -33,17 +33,33 @@
                     setTimeout(function() { if (window.testRunner) testRunner.notifyDone(); }, 200);
                 }
             }
-                
+
+            function pagehide()
+            {
+                // Have the video element generate some events that need to be suspended.
+                // We use multiple events to verify that they fire in the correct order.
+                consoleWrite("*** Changing playbackRate just before going into cache, to schedule a ratechange event.");
+                document.getElementsByTagName("video")[0].playbackRate = 2; 
+                consoleWrite("*** Changing volume just before going into cache, to schedule a volumechange event.");
+                document.getElementsByTagName("video")[0].volume = 0.5; 
+            }
+
             function canplaythrough() 
             {
                 testExpected("loadCount", 0);
                 if (!loadCount) {
+                    video = document.getElementsByTagName('video')[0];
                     consoleWrite("*** Page going into cache");
                     setTimeout('window.location = "data:text/html,<script>history.back()<" + "/script>"', 0);
                 }
                 ++loadCount;
             }
 
+            function propertyChangeCallback(evt)
+            {
+                consoleWrite("*** " + evt.type +  " event fired. This should happen AFTER returning from cache.");
+            }
+
             function setup()
             {
                 video = mediaElement = document.getElementsByTagName('video')[0];
@@ -52,9 +68,13 @@
                 waitForEvent('canplaythrough', canplaythrough);
                 
                 video.src = ""
+
+                video._onratechange_ = propertyChangeCallback;
+                video._onvolumechange_ = propertyChangeCallback;
             }
 
             window._onpageshow_ = pageshow;
+            window._onpagehide_ = pagehide;
         </script>
     </head>
     <body>

Modified: trunk/Source/WebCore/ChangeLog (187030 => 187031)


--- trunk/Source/WebCore/ChangeLog	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/Source/WebCore/ChangeLog	2015-07-20 20:16:18 UTC (rev 187031)
@@ -1,3 +1,53 @@
+2015-07-20  Andreas Kling  <[email protected]>
+
+        Improve behavior of media elements in page cache.
+        <https://webkit.org/b/147020>
+        <rdar://problem/21712311>
+
+        Reviewed by Chris Dumez.
+
+        Make improvements for media elements when transitioning in/out of page cache:
+
+        - Events that were scheduled when going into cache will now be delivered
+          when the page is restored from cache.
+
+        - Data buffering is turned off while in the cache. This reduces the memory
+          cost of cached pages with media elements on iOS (where mediaserverd would
+          keep upcoming video frames in memory for cached pages.)
+
+        Test: media/restore-from-page-cache.html (amended)
+
+        * dom/GenericEventQueue.h:
+        * dom/GenericEventQueue.cpp:
+        (WebCore::GenericEventQueue::enqueueEvent):
+        (WebCore::GenericEventQueue::suspend):
+        (WebCore::GenericEventQueue::resume):
+
+            Add a simple suspend/resume mechanism to GenericEventQueue that can
+            be used to support page caching.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::stop):
+        (WebCore::HTMLMediaElement::suspend):
+        (WebCore::HTMLMediaElement::resume):
+        (WebCore::HTMLMediaElement::stopWithoutDestroyingMediaPlayer):
+
+            Adapt to event queueing changes and add calls to setShouldBufferData().
+
+        * html/HTMLSourceElement.h:
+        * html/HTMLSourceElement.cpp:
+        (WebCore::HTMLSourceElement::HTMLSourceElement):
+        (WebCore::HTMLSourceElement::create):
+        (WebCore::HTMLSourceElement::activeDOMObjectName):
+        (WebCore::HTMLSourceElement::canSuspendForPageCache):
+        (WebCore::HTMLSourceElement::suspend):
+        (WebCore::HTMLSourceElement::resume):
+        (WebCore::HTMLSourceElement::stop):
+
+            Turn HTMLSourceElement into an ActiveDOMObject so it gets all the
+            appropriate page cache notifications directly. Suspend the delayed
+            error event delivery timer when cached.
+
 2015-07-20  Mark Lam  <[email protected]>
 
         Rollout r187020 and r187021: breaks JSC API tests on debug builds.

Modified: trunk/Source/WebCore/dom/GenericEventQueue.cpp (187030 => 187031)


--- trunk/Source/WebCore/dom/GenericEventQueue.cpp	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/Source/WebCore/dom/GenericEventQueue.cpp	2015-07-20 20:16:18 UTC (rev 187031)
@@ -45,20 +45,22 @@
 {
 }
 
-bool GenericEventQueue::enqueueEvent(PassRefPtr<Event> event)
+void GenericEventQueue::enqueueEvent(PassRefPtr<Event> event)
 {
     if (m_isClosed)
-        return false;
+        return;
 
     if (event->target() == &m_owner)
         event->setTarget(0);
 
     m_pendingEvents.append(event);
+
+    if (m_isSuspended)
+        return;
+
     pendingQueues().append(m_weakPtrFactory.createWeakPtr());
     if (!sharedTimer().isActive())
         sharedTimer().startOneShot(0);
-
-    return true;
 }
 
 Timer& GenericEventQueue::sharedTimer()
@@ -120,4 +122,26 @@
     return !m_pendingEvents.isEmpty();
 }
 
+void GenericEventQueue::suspend()
+{
+    ASSERT(!m_isSuspended);
+    m_isSuspended = true;
+    m_weakPtrFactory.revokeAll();
 }
+
+void GenericEventQueue::resume()
+{
+    ASSERT(m_isSuspended);
+    m_isSuspended = false;
+
+    if (m_pendingEvents.isEmpty())
+        return;
+
+    for (unsigned i = 0; i < m_pendingEvents.size(); ++i)
+        pendingQueues().append(m_weakPtrFactory.createWeakPtr());
+
+    if (!sharedTimer().isActive())
+        sharedTimer().startOneShot(0);
+}
+
+}

Modified: trunk/Source/WebCore/dom/GenericEventQueue.h (187030 => 187031)


--- trunk/Source/WebCore/dom/GenericEventQueue.h	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/Source/WebCore/dom/GenericEventQueue.h	2015-07-20 20:16:18 UTC (rev 187031)
@@ -42,12 +42,15 @@
     explicit GenericEventQueue(EventTarget&);
     ~GenericEventQueue();
 
-    bool enqueueEvent(PassRefPtr<Event>);
+    void enqueueEvent(PassRefPtr<Event>);
     void close();
 
     void cancelAllEvents();
     bool hasPendingEvents() const;
 
+    void suspend();
+    void resume();
+
 private:
     static Timer& sharedTimer();
     static void sharedTimerFired();
@@ -59,6 +62,7 @@
     Deque<RefPtr<Event>> m_pendingEvents;
     WeakPtrFactory<GenericEventQueue> m_weakPtrFactory;
     bool m_isClosed;
+    bool m_isSuspended { false };
 };
 
 }

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (187030 => 187031)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2015-07-20 20:16:18 UTC (rev 187031)
@@ -4890,10 +4890,7 @@
         renderer()->updateFromElement();
     
     stopPeriodicTimers();
-    cancelPendingEventsAndCallbacks();
 
-    m_asyncEventQueue.close();
-
     updateSleepDisabling();
 }
 
@@ -4903,6 +4900,8 @@
 
     stopWithoutDestroyingMediaPlayer();
 
+    m_asyncEventQueue.close();
+
     // Once an active DOM object has been stopped it can not be restarted, so we can deallocate
     // the media player now. Note that userCancelledLoad will already called clearMediaPlayer
     // if the media was not fully loaded, but we need the same cleanup if the file was completely
@@ -4918,6 +4917,8 @@
     {
         case PageCache:
             stopWithoutDestroyingMediaPlayer();
+            m_asyncEventQueue.suspend();
+            setShouldBufferData(false);
             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequirePageConsentToResumeMedia);
             break;
         case DocumentWillBePaused:
@@ -4935,6 +4936,10 @@
 
     m_inActiveDocument = true;
 
+    m_asyncEventQueue.resume();
+
+    setShouldBufferData(true);
+
     if (!m_mediaSession->pageAllowsPlaybackAfterResuming(*this))
         document().addMediaCanStartListener(this);
     else

Modified: trunk/Source/WebCore/html/HTMLSourceElement.cpp (187030 => 187031)


--- trunk/Source/WebCore/html/HTMLSourceElement.cpp	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/Source/WebCore/html/HTMLSourceElement.cpp	2015-07-20 20:16:18 UTC (rev 187031)
@@ -40,6 +40,7 @@
 
 inline HTMLSourceElement::HTMLSourceElement(const QualifiedName& tagName, Document& document)
     : HTMLElement(tagName, document)
+    , ActiveDOMObject(&document)
     , m_errorEventTimer(*this, &HTMLSourceElement::errorEventTimerFired)
 {
     LOG(Media, "HTMLSourceElement::HTMLSourceElement - %p", this);
@@ -48,7 +49,9 @@
 
 Ref<HTMLSourceElement> HTMLSourceElement::create(const QualifiedName& tagName, Document& document)
 {
-    return adoptRef(*new HTMLSourceElement(tagName, document));
+    Ref<HTMLSourceElement> sourceElement = adoptRef(*new HTMLSourceElement(tagName, document));
+    sourceElement->suspendIfNeeded();
+    return sourceElement;
 }
 
 Node::InsertionNotificationRequest HTMLSourceElement::insertedInto(ContainerNode& insertionPoint)
@@ -121,6 +124,37 @@
     return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
 }
 
+const char* HTMLSourceElement::activeDOMObjectName() const
+{
+    return "HTMLSourceElement";
 }
 
+bool HTMLSourceElement::canSuspendForPageCache() const
+{
+    return true;
+}
+
+void HTMLSourceElement::suspend(ReasonForSuspension why)
+{
+    if (why == PageCache) {
+        m_shouldRescheduleErrorEventOnResume = m_errorEventTimer.isActive();
+        m_errorEventTimer.stop();
+    }
+}
+
+void HTMLSourceElement::resume()
+{
+    if (m_shouldRescheduleErrorEventOnResume) {
+        m_errorEventTimer.startOneShot(0);
+        m_shouldRescheduleErrorEventOnResume = false;
+    }
+}
+
+void HTMLSourceElement::stop()
+{
+    cancelPendingErrorEvent();
+}
+
+}
+
 #endif

Modified: trunk/Source/WebCore/html/HTMLSourceElement.h (187030 => 187031)


--- trunk/Source/WebCore/html/HTMLSourceElement.h	2015-07-20 20:06:27 UTC (rev 187030)
+++ trunk/Source/WebCore/html/HTMLSourceElement.h	2015-07-20 20:16:18 UTC (rev 187031)
@@ -32,7 +32,7 @@
 
 namespace WebCore {
 
-class HTMLSourceElement final : public HTMLElement {
+class HTMLSourceElement final : public HTMLElement, public ActiveDOMObject {
 public:
     static Ref<HTMLSourceElement> create(const QualifiedName&, Document&);
 
@@ -52,9 +52,17 @@
     virtual void removedFrom(ContainerNode&) override;
     virtual bool isURLAttribute(const Attribute&) const override;
 
+    // ActiveDOMObject.
+    const char* activeDOMObjectName() const override;
+    bool canSuspendForPageCache() const override;
+    void suspend(ReasonForSuspension) override;
+    void resume() override;
+    void stop() override;
+
     void errorEventTimerFired();
 
     Timer m_errorEventTimer;
+    bool m_shouldRescheduleErrorEventOnResume { false };
 };
 
 } //namespace
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to