2009-08-20 Eric Carlson <eric.carlson@apple.com>
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Aug 2009 17:21:35 +0000 (17:21 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Aug 2009 17:21:35 +0000 (17:21 +0000)
        Reviewed by Anders Carlsson.

        HTML5 media elements must fire 'loadend' progress event
        https://bugs.webkit.org/show_bug.cgi?id=28419

        * dom/EventNames.h:
            Define loadend.

        * html/HTMLMediaElement.cpp:
        (WebCore::HTMLMediaElement::parseMappedAttribute):
            Deal with onloadend.
        (WebCore::HTMLMediaElement::loadInternal):
            Post 'loadend' immediately after 'abort'.
        (WebCore::HTMLMediaElement::noneSupported):
            Post 'loadend' immediately after 'error'.
        (WebCore::HTMLMediaElement::mediaEngineError):
            Ditto.
        (WebCore::HTMLMediaElement::setNetworkState):
            Post 'loadend' immediately after 'load'.
        (WebCore::HTMLMediaElement::userCancelledLoad):
            Post 'loadend' immediately after 'abort'.

2009-08-20  Eric Carlson  <eric.carlson@apple.com>

        Reviewed by Anders Carlsson.

        HTML5 media elements must fire 'loadend' progress event
        https://bugs.webkit.org/show_bug.cgi?id=28419

        Add 'loadend' to existing tests.

        * media/event-attributes-expected.txt:
        * media/event-attributes.html:
        * media/media-load-event-expected.txt:
        * media/media-load-event.html:
        * media/progress-event-at-least-one-expected.txt:
        * media/progress-event-at-least-one.html:

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

LayoutTests/ChangeLog
LayoutTests/media/event-attributes-expected.txt
LayoutTests/media/event-attributes.html
LayoutTests/media/media-load-event-expected.txt
LayoutTests/media/media-load-event.html
LayoutTests/media/progress-event-at-least-one-expected.txt
LayoutTests/media/progress-event-at-least-one.html
WebCore/ChangeLog
WebCore/dom/EventNames.h
WebCore/html/HTMLMediaElement.cpp

index 7f94f1a..01f71c4 100644 (file)
@@ -1,3 +1,19 @@
+2009-08-20  Eric Carlson  <eric.carlson@apple.com>
+
+        Reviewed by Anders Carlsson.
+
+        HTML5 media elements must fire 'loadend' progress event
+        https://bugs.webkit.org/show_bug.cgi?id=28419
+
+        Add 'loadend' to existing tests.
+
+        * media/event-attributes-expected.txt:
+        * media/event-attributes.html:
+        * media/media-load-event-expected.txt:
+        * media/media-load-event.html:
+        * media/progress-event-at-least-one-expected.txt:
+        * media/progress-event-at-least-one.html:
+
 2009-08-20  Ryosuke Niwa  <rniwa@webkit.org>
 
         Reviewed by Darin Adler.
index 2fecdf5..4ea5fbd 100644 (file)
@@ -5,29 +5,37 @@ EVENT(loadeddata)
 EVENT(canplay)
 EVENT(canplaythrough)
 EVENT(load)
+EVENT(loadend)
 
+*** starting playback
 RUN(video.play())
 EVENT(play)
 EVENT(playing)
 
+*** changing playback rate
 RUN(video.playbackRate = 2)
 EVENT(ratechange)
 
+*** setting volume
 RUN(video.volume = 0.5)
 EVENT(volumechange)
 
+*** pausing playback
 RUN(video.pause())
 EVENT(pause)
 
+*** seeking
 RUN(video.currentTime = 5.6)
 EVENT(seeked)
 
+*** beginning playback
 RUN(video.play())
 EVENT(ratechange)
 EVENT(play)
 EVENT(playing)
 EVENT(ended)
 
+*** played to end, setting 'src' to an invalid movie
 RUN(video.src = 'content/garbage.mp4')
 RUN(video.load())
 EVENT(emptied)
index 88d2b59..f7d1758 100644 (file)
                     consoleWrite("EVENT(" + event.type + ")");
                 switch (event.type)
                 {
-                    case "load":
-                        consoleWrite("");
+                    case "loadend":
+                        consoleWrite("<br>*** starting playback");
                         run("video.play()"); 
                         break;
                     case "playing":
                         if (++playingCount == 1) {
-                            consoleWrite("");
+                            consoleWrite("<br>*** changing playback rate");
                             run("video.playbackRate = 2");
                         }
                         break;
                     case "ratechange":
                         if (++ratechangeCount == 1) {
-                            consoleWrite("");
+                            consoleWrite("<br>*** setting volume");
                             run("video.volume = 0.5");
                         }
                         break;
                     case "volumechange":
-                        consoleWrite("");
+                        consoleWrite("<br>*** pausing playback");
                         run("video.pause()");
                         break;
                     case "pause":
-                        consoleWrite("");
+                        consoleWrite("<br>*** seeking");
                         run("video.currentTime = 5.6");
                         break;
                     case "seeked":
-                        consoleWrite("");
+                        consoleWrite("<br>*** beginning playback");
                         run("video.play()");
                         break;
                     case "ended":
-                        consoleWrite("");
+                        consoleWrite("<br>*** played to end, setting 'src' to an invalid movie");
                         run("video.src = 'content/garbage.mp4'");
                         run("video.load()");
                         break;
@@ -58,7 +58,6 @@
                     default:
                         break;
                 }
-                        
             }
 
             function start()
@@ -83,6 +82,7 @@
             onloadeddata="eventHandler()"
             onloadedmetadata="eventHandler()"
             onloadstart="eventHandler()"
+            onloadend="eventHandler()"
             onpause="eventHandler()"
             onplay="eventHandler()"
             onplaying="eventHandler()"
index 9563939..53609f9 100644 (file)
@@ -8,11 +8,13 @@ EVENT(loadstart)
 EVENT(durationchange)
 EVENT(loadeddata)
 EVENT(load)
+EVENT(loadend)
 
 RUN(document.getElementById('parent').appendChild(mediaElement))
 RUN(mediaElement.play())
 
 EVENT(play)
 EVENT(playing)
+
 END OF TEST
 
index 778c3fa..0f6b2a0 100644 (file)
@@ -4,13 +4,18 @@
 
         <script>
 
-            function loaded()
+            function playing()
             {
-                consoleWrite("EVENT(load)");
+                consoleWrite("EVENT(playing)<br>");
+                endTest();
+            }
+
+            function loadend()
+            {
+                consoleWrite("EVENT(loadend)");
                 consoleWrite("");
                 run("document.getElementById('parent').appendChild(mediaElement)");
                 run("mediaElement.play()");
-                window.setTimeout(endTest, 250);
                 consoleWrite("");
             }
 
             {
                 run("mediaElement = document.createElement('audio')");
 
-                mediaElement.onload = loaded;
+                mediaElement.setAttribute('onloadend', "loadend()");
+                mediaElement.setAttribute('onplaying', "playing()");
 
                 waitForEvent("loadstart");
+                waitForEvent("load");
                 waitForEvent("waiting");
                 waitForEvent("ratechange");
                 waitForEvent("durationchange");
                 waitForEvent("pause");
                 waitForEvent("play");
-                waitForEvent("playing");
                 waitForEvent('loadeddata');
 
                 run("mediaElement.src = 'content/test.wav'");
index 8d22524..fd34659 100644 (file)
@@ -8,7 +8,7 @@ EVENT(loadstart)
 EVENT(durationchange)
 EVENT(loadedmetadata)
 EVENT(loadeddata)
-EVENT(load)
+EVENT(loadend)
 EXPECTED (progressEventCount >= '1') OK
 END OF TEST
 
index b310a43..f76cd94 100644 (file)
@@ -17,9 +17,9 @@
                     return;
             }
 
-            function loaded()
+            function loadend()
             {
-                consoleWrite("EVENT(load)");
+                consoleWrite("EVENT(loadend)");
                 
                 testExpected('progressEventCount', 1, '>=');
                 endTest();
             {
                 run("mediaElement = document.createElement('audio')");
 
-                mediaElement.setAttribute('onload', "loaded()");
+                mediaElement.setAttribute('onloadend', "loadend()");
                 mediaElement.setAttribute('onprogress', "progress()");
 
                 waitForEvent("loadstart");
+                waitForEvent("loadend");
+                waitForEvent("loaded");
                 waitForEvent("waiting");
                 waitForEvent("ratechange");
                 waitForEvent("durationchange");
index 3938bed..e8ec9c5 100644 (file)
@@ -1,3 +1,27 @@
+2009-08-20  Eric Carlson  <eric.carlson@apple.com>
+
+        Reviewed by Anders Carlsson.
+
+        HTML5 media elements must fire 'loadend' progress event
+        https://bugs.webkit.org/show_bug.cgi?id=28419
+
+        * dom/EventNames.h:
+            Define loadend.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::parseMappedAttribute):
+            Deal with onloadend.
+        (WebCore::HTMLMediaElement::loadInternal):
+            Post 'loadend' immediately after 'abort'.
+        (WebCore::HTMLMediaElement::noneSupported):
+            Post 'loadend' immediately after 'error'.
+        (WebCore::HTMLMediaElement::mediaEngineError):
+            Ditto.
+        (WebCore::HTMLMediaElement::setNetworkState):
+            Post 'loadend' immediately after 'load'.
+        (WebCore::HTMLMediaElement::userCancelledLoad):
+            Post 'loadend' immediately after 'abort'.
+
 2009-08-20  Ryosuke Niwa  <rniwa@webkit.org>
 
         Reviewed by Darin Adler.
index e0b8e7a..9e54190 100644 (file)
@@ -61,6 +61,7 @@ namespace WebCore {
     macro(keypress) \
     macro(keyup) \
     macro(load) \
+    macro(loadend) \
     macro(loadstart) \
     macro(message) \
     macro(mousedown) \
index 17fe622..230a80c 100644 (file)
@@ -179,6 +179,8 @@ void HTMLMediaElement::parseMappedAttribute(MappedAttribute* attr)
         setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr));
     else if (attrName == onloadedmetadataAttr)
         setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr));
+    else if (attrName == onloadendAttr)
+        setAttributeEventListener(eventNames().loadendEvent, createAttributeEventListener(this, attr));
     else if (attrName == onloadstartAttr)
         setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr));
     else if (attrName == onpauseAttr)
@@ -432,20 +434,23 @@ void HTMLMediaElement::loadInternal()
     // one of the task queues, then remove those tasks.
     cancelPendingEventsAndCallbacks();
     
-    // 4 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, set the 
-    // error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED, 
-    // and fire a progress event called abort at the media element.
+    // 4 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, set
+    // the error attribute to a new MediaError object whose code attribute is set to
+    // MEDIA_ERR_ABORTED, fire a progress event called abort at the media element, in the
+    // context of the fetching process that is in progress for the element, and fire a progress
+    // event called loadend at the media element, in the context of the same fetching process.
     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) {
         m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
-        
-        // fire synchronous 'abort'
+
+        // fire synchronous 'abort' and 'loadend'
         bool totalKnown = m_player && m_player->totalBytesKnown();
         unsigned loaded = m_player ? m_player->bytesLoaded() : 0;
         unsigned total = m_player ? m_player->totalBytes() : 0;
         dispatchProgressEvent(eventNames().abortEvent, totalKnown, loaded, total);
+        dispatchProgressEvent(eventNames().loadendEvent, totalKnown, loaded, total);
     }
-    
-    // 5 
+
+    // 5
     m_error = 0;
     m_autoplaying = true;
     m_playedTimeRanges = TimeRanges::create();
@@ -453,7 +458,7 @@ void HTMLMediaElement::loadInternal()
 
     // 6
     setPlaybackRate(defaultPlaybackRate());
-    
+
     // 7
     if (m_networkState != NETWORK_EMPTY) {
         m_networkState = NETWORK_EMPTY;
@@ -467,7 +472,7 @@ void HTMLMediaElement::loadInternal()
         }
         dispatchEvent(eventNames().emptiedEvent, false, true);
     }
-    
+
     selectMediaResource();
     m_processingLoad = false;
 }
@@ -592,22 +597,29 @@ void HTMLMediaElement::noneSupported()
     m_loadState = WaitingForSource;
     m_currentSourceNode = 0;
 
-    // 3 - Reaching this step indicates that either the URL failed to resolve, or the media 
-    // resource failed to load. Set the error attribute to a new MediaError object whose 
+    // 4 - Reaching this step indicates that either the URL failed to resolve, or the media
+    // resource failed to load. Set the error attribute to a new MediaError object whose
     // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED.
     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
 
-    // 4- Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
+    // - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
     m_networkState = NETWORK_NO_SOURCE;
 
-    // 5 - Queue a task to fire a progress event called error at the media element.
-    scheduleProgressEvent(eventNames().errorEvent); 
+    // 6 - Queue a task to fire a progress event called error at the media element, in
+    // the context of the fetching process that was used to try to obtain the media
+    // resource in the resource fetch algorithm.
+    scheduleProgressEvent(eventNames().errorEvent);
 
-    // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
+    // 7 - Queue a task to fire a progress event called loadend at the media element, in
+    // the context of the fetching process that was used to try to obtain the media
+    // resource in the resource fetch algorithm.
+    scheduleProgressEvent(eventNames().loadendEvent);
+
+    // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
     m_delayingTheLoadEvent = false;
 
-    // Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource.
-    
+    // 9 -Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource.
+
     if (isVideo())
         static_cast<HTMLVideoElement*>(this)->updatePosterImage();
     if (renderer())
@@ -624,20 +636,24 @@ void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
     m_error = err;
 
-    // 3 - Queue a task to fire a progress event called error at the media element.
-    scheduleProgressEvent(eventNames().errorEvent); 
+    // 3 - Queue a task to fire a progress event called error at the media element, in
+    // the context of the fetching process started by this instance of this algorithm.
+    scheduleProgressEvent(eventNames().errorEvent);
+
+    // 4 - Queue a task to fire a progress event called loadend at the media element, in
+    // the context of the fetching process started by this instance of this algorithm.
+    scheduleProgressEvent(eventNames().loadendEvent);
 
-    // 3 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a 
+    // 5 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
     // task to fire a simple event called emptied at the element.
     m_networkState = NETWORK_EMPTY;
     scheduleEvent(eventNames().emptiedEvent);
 
-    // 4 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
+    // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
     m_delayingTheLoadEvent = false;
 
-    // 5 - Abort the overall resource selection algorithm.
+    // 7 - Abort the overall resource selection algorithm.
     m_currentSourceNode = 0;
-
 }
 
 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
@@ -722,7 +738,8 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
             if (static_cast<ReadyState>(currentState) != m_readyState)
                 setReadyState(currentState);
 
-             scheduleProgressEvent(eventNames().loadEvent); 
+            scheduleProgressEvent(eventNames().loadEvent);
+            scheduleProgressEvent(eventNames().loadendEvent);
         }
     }
 }
@@ -1562,12 +1579,17 @@ void HTMLMediaElement::userCancelledLoad()
         // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORT.
         m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
 
-        // 3 - Queue a task to fire a progress event called abort at the media element.
+        // 3 - Queue a task to fire a progress event called abort at the media element, in the context
+        // of the fetching process started by this instance of this algorithm.
         scheduleProgressEvent(eventNames().abortEvent);
 
-        // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the 
-        // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a 
-        // simple event called emptied at the element. Otherwise, set set the element's networkState 
+        // 4 - Queue a task to fire a progress event called loadend at the media element, in the context
+        // of the fetching process started by this instance of this algorithm.
+        scheduleProgressEvent(eventNames().loadendEvent);
+
+        // 5 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
+        // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
+        // simple event called emptied at the element. Otherwise, set set the element's networkState
         // attribute to the NETWORK_IDLE value.
         if (m_networkState >= NETWORK_LOADING) {
             m_networkState = NETWORK_EMPTY;
@@ -1575,8 +1597,11 @@ void HTMLMediaElement::userCancelledLoad()
             scheduleEvent(eventNames().emptiedEvent);
         }
 
-        // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
+        // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
         m_delayingTheLoadEvent = false;
+
+        // 7 - Abort the overall resource selection algorithm.
+        m_currentSourceNode = 0;
     }
 }
 
@@ -1608,7 +1633,7 @@ void HTMLMediaElement::documentDidBecomeActive()
         ExceptionCode ec;
         load(ec);
     }
-        
+
     if (renderer())
         renderer()->updateFromElement();
 }