<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[280468] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/280468">280468</a></dd>
<dt>Author</dt> <dd>Tomoki.Imai@sony.com</dd>
<dt>Date</dt> <dd>2021-07-29 23:43:48 -0700 (Thu, 29 Jul 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>Missing playing events when the ready state becomes HAVE_FUTURE_DATA/HAVE_ENOUGH_DATA from HAVE_METADATA state
https://bugs.webkit.org/show_bug.cgi?id=228531

Reviewed by Eric Carlson.

Source/WebCore:

The main issue is that missing playing event when the ready state becomes HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA from HAVE_METADATA.
According to the specification, we need to "notify about playing" in the following cases:
- If the previous ready state was HAVE_CURRENT_DATA or less, and the new ready state is HAVE_FUTURE_DATA, and it's not paused.
- If the new ready state is HAVE_ENOUGH_DATA, and it's eligible for autoplay
The implementation didn't cover these cases and had web compatibility issues.

We also should move scheduleNotifyAboutPlaying from setPlaying to playInternal and checks the ready state.
- Without this change, playing event is fired twice. The first one is fired by setReadyState, and the second is called from setPlaying.
- According to the specification, scheduleNotifyAboutPlaying should be in  "internal play steps" and check the ready state.
  Checking ready state fixes the issue where playing event is fired twice.

Test: media/media-source/media-source-monitor-playing-event.html

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::setReadyState): Added missing scheduleNotifyAboutPlaying calls. Added do-while to make the implementation similar to the specification text
(WebCore::HTMLMediaElement::playInternal): Added scheduleNotifyAboutPlaying call.
                                           According to the specification, "internal play steps" should "notify about playing" when
                                           the ready state is HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
(WebCore::HTMLMediaElement::setPlaying): Removed scheduleNotifyAboutPlaying call because playInternal now calls scheduleNotifyAboutPlaying instead.

Reference:
- https://html.spec.whatwg.org/multipage/media.html#internal-play-steps
- https://html.spec.whatwg.org/multipage/media.html#ready-states

LayoutTests:

Added the testcase to checks if the playing event is fired correctly on the ready state changes.

* media/media-source/media-source-monitor-playing-event-expected.txt: Added.
* media/media-source/media-source-monitor-playing-event.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaElementcpp">trunk/Source/WebCore/html/HTMLMediaElement.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsmediamediasourcemediasourcemonitorplayingeventexpectedtxt">trunk/LayoutTests/media/media-source/media-source-monitor-playing-event-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediasourcemediasourcemonitorplayingeventhtml">trunk/LayoutTests/media/media-source/media-source-monitor-playing-event.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (280467 => 280468)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2021-07-30 03:53:49 UTC (rev 280467)
+++ trunk/LayoutTests/ChangeLog 2021-07-30 06:43:48 UTC (rev 280468)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2021-07-29  Tomoki Imai  <Tomoki.Imai@sony.com>
+
+        Missing playing events when the ready state becomes HAVE_FUTURE_DATA/HAVE_ENOUGH_DATA from HAVE_METADATA state
+        https://bugs.webkit.org/show_bug.cgi?id=228531
+
+        Reviewed by Eric Carlson.
+
+        Added the testcase to checks if the playing event is fired correctly on the ready state changes.
+
+        * media/media-source/media-source-monitor-playing-event-expected.txt: Added.
+        * media/media-source/media-source-monitor-playing-event.html: Added.
+
</ins><span class="cx"> 2021-07-29  Myles C. Maxfield  <mmaxfield@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Stop building WebGPU and the WHLSL compiler to decrease binary size
</span></span></pre></div>
<a id="trunkLayoutTestsmediamediasourcemediasourcemonitorplayingeventexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-source/media-source-monitor-playing-event-expected.txt (0 => 280468)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/media-source/media-source-monitor-playing-event-expected.txt                             (rev 0)
+++ trunk/LayoutTests/media/media-source/media-source-monitor-playing-event-expected.txt        2021-07-30 06:43:48 UTC (rev 280468)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+This test checks if the playing event fires when the ready state changes from HAVE_METADATA to HAVE_FUTURE_DATA or higher.
+
+EXPECTED (source.readyState == 'closed') OK
+EVENT(loadstart)
+EVENT(sourceopen)
+RUN(sourceBuffer.appendBuffer(initSegment))
+EVENT(loadedmetadata)
+EVENT(updateend)
+video.readyState : HAVE_METADATA
+RUN(sourceBuffer.appendBuffer(sample))
+EVENT(updateend)
+video.readyState : HAVE_FUTURE_DATA
+RUN(sourceBuffer.appendBuffer(sample))
+EVENT(loadeddata)
+EVENT(canplay)
+EVENT(updateend)
+EVENT(canplaythrough)
+EVENT(playing)
+video.readyState : HAVE_ENOUGH_DATA
+RUN(sourceBuffer.remove(0,10))
+EVENT(updateend)
+EVENT(waiting)
+video.readyState : HAVE_METADATA
+RUN(sourceBuffer.appendBuffer(sample))
+EVENT(updateend)
+video.readyState : HAVE_METADATA
+RUN(sourceBuffer.appendBuffer(sample))
+EVENT(updateend)
+EVENT(canplay)
+EVENT(playing)
+video.readyState : HAVE_ENOUGH_DATA
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediasourcemediasourcemonitorplayingeventhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-source/media-source-monitor-playing-event.html (0 => 280468)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/media-source/media-source-monitor-playing-event.html                             (rev 0)
+++ trunk/LayoutTests/media/media-source/media-source-monitor-playing-event.html        2021-07-30 06:43:48 UTC (rev 280468)
</span><span class="lines">@@ -0,0 +1,92 @@
</span><ins>+<!DOCTYPE html>
+<html>
+<head>
+    <title>media-source-monitor-playing-event</title>
+    <script src="mock-media-source.js"></script>
+    <script src="../video-test.js"></script>
+    <script>
+    var source;
+    var sourceBuffer;
+    var initSegment;
+    var sample;
+    var handleVideoEvents = [
+        "loadstart",
+        "waiting",
+        "loadedmetadata",
+        "loadeddata",
+        "canplay",
+        "canplaythrough",
+        "pause",
+        "ended",
+    ];
+    var readyStateString = [
+        "HAVE_NOTHING",
+        "HAVE_METADATA",
+        "HAVE_CURRENT_DATA",
+        "HAVE_FUTURE_DATA",
+        "HAVE_ENOUGH_DATA"
+    ];
+
+    if (window.internals)
+        internals.initializeMockMediaSource();
+
+    window.addEventListener('load', async() => {
+        findMediaElement();
+
+        for (var i = 0; i < handleVideoEvents.length ; i++)
+            waitForEvent(handleVideoEvents[i]);
+
+        source = new MediaSource();
+        testExpected('source.readyState', 'closed');
+
+        const videoSource = document.createElement('source');
+        videoSource.type = 'video/mock; codecs=mock';
+        videoSource.src = URL.createObjectURL(source);
+        video.appendChild(videoSource);
+        await waitFor(source, 'sourceopen');
+
+        sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock");
+        initSegment = makeAInit(100, [makeATrack(1, 'mock', TRACK_KIND.VIDEO)]);
+        run('sourceBuffer.appendBuffer(initSegment)');
+        await waitFor(sourceBuffer, 'updateend');
+
+        consoleWrite('video.readyState : ' + readyStateString[video.readyState]);
+        sample = makeASample(0, 0, 1, 1, 1, SAMPLE_FLAG.SYNC, 1);
+        // This append changes ready state to HAVE_FUTURE_DATA.
+        run('sourceBuffer.appendBuffer(sample)');
+        await waitFor(sourceBuffer, 'updateend');
+
+        consoleWrite('video.readyState : ' + readyStateString[video.readyState]);
+        sample = makeASample(1, 1, 9, 1, 1, SAMPLE_FLAG.SYNC, 1);
+        // This append changes the ready state to HAVE_ENOUGH_DATA and fires the playing event.
+        run('sourceBuffer.appendBuffer(sample)');
+        await Promise.all([waitFor(mediaElement, 'playing'), waitFor(sourceBuffer, 'updateend')]);
+
+        consoleWrite('video.readyState : ' + readyStateString[video.readyState]);
+        // This remove changes ready state to HAVE_METADATA.
+        run('sourceBuffer.remove(0,10)');
+        await waitFor(sourceBuffer, 'updateend');
+
+        await sleepFor(1000);
+
+        consoleWrite('video.readyState : ' + readyStateString[video.readyState]);
+        sample = makeASample(0, 0, 1, 1, 1, SAMPLE_FLAG.SYNC, 1);
+        run('sourceBuffer.appendBuffer(sample)');
+        await waitFor(sourceBuffer, 'updateend');
+
+        consoleWrite('video.readyState : ' + readyStateString[video.readyState]);
+        sample = makeASample(1, 1, 9, 1, 1, SAMPLE_FLAG.SYNC, 1);
+        // This append changes the ready state to HAVE_ENOUGH_DATA and fires the playing event.
+        run('sourceBuffer.appendBuffer(sample)');
+        await Promise.all([waitFor(mediaElement, 'playing'), waitFor(sourceBuffer, 'updateend')]);
+
+        consoleWrite('video.readyState : ' + readyStateString[video.readyState]);
+        endTest();
+    });
+    </script>
+</head>
+<body>
+    <div>This test checks if the playing event fires when the ready state changes from HAVE_METADATA to HAVE_FUTURE_DATA or higher.</div>
+    <video autoplay></video>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (280467 => 280468)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-07-30 03:53:49 UTC (rev 280467)
+++ trunk/Source/WebCore/ChangeLog      2021-07-30 06:43:48 UTC (rev 280468)
</span><span class="lines">@@ -1,3 +1,34 @@
</span><ins>+2021-07-29  Tomoki Imai  <Tomoki.Imai@sony.com>
+
+        Missing playing events when the ready state becomes HAVE_FUTURE_DATA/HAVE_ENOUGH_DATA from HAVE_METADATA state
+        https://bugs.webkit.org/show_bug.cgi?id=228531
+
+        Reviewed by Eric Carlson.
+
+        The main issue is that missing playing event when the ready state becomes HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA from HAVE_METADATA.
+        According to the specification, we need to "notify about playing" in the following cases:
+        - If the previous ready state was HAVE_CURRENT_DATA or less, and the new ready state is HAVE_FUTURE_DATA, and it's not paused.
+        - If the new ready state is HAVE_ENOUGH_DATA, and it's eligible for autoplay
+        The implementation didn't cover these cases and had web compatibility issues.
+
+        We also should move scheduleNotifyAboutPlaying from setPlaying to playInternal and checks the ready state.
+        - Without this change, playing event is fired twice. The first one is fired by setReadyState, and the second is called from setPlaying.
+        - According to the specification, scheduleNotifyAboutPlaying should be in  "internal play steps" and check the ready state.
+          Checking ready state fixes the issue where playing event is fired twice.
+
+        Test: media/media-source/media-source-monitor-playing-event.html
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::setReadyState): Added missing scheduleNotifyAboutPlaying calls. Added do-while to make the implementation similar to the specification text
+        (WebCore::HTMLMediaElement::playInternal): Added scheduleNotifyAboutPlaying call.
+                                                   According to the specification, "internal play steps" should "notify about playing" when
+                                                   the ready state is HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
+        (WebCore::HTMLMediaElement::setPlaying): Removed scheduleNotifyAboutPlaying call because playInternal now calls scheduleNotifyAboutPlaying instead.
+
+        Reference:
+        - https://html.spec.whatwg.org/multipage/media.html#internal-play-steps
+        - https://html.spec.whatwg.org/multipage/media.html#ready-states
+
</ins><span class="cx"> 2021-07-29  Myles C. Maxfield  <mmaxfield@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Stop building WebGPU and the WHLSL compiler to decrease binary size
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (280467 => 280468)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.cpp   2021-07-30 03:53:49 UTC (rev 280467)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp      2021-07-30 06:43:48 UTC (rev 280468)
</span><span class="lines">@@ -2403,65 +2403,105 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
-        prepareMediaFragmentURI();
-        scheduleEvent(eventNames().durationchangeEvent);
-        scheduleResizeEvent();
-        scheduleEvent(eventNames().loadedmetadataEvent);
</del><ins>+    // Apply the first applicable set of substeps from the following list:
+    do {
+        // FIXME: The specification seems to only say HAVE_METADATA
+        // explicitly (rather than or higher) for this state. It's unclear
+        // if/how things like loadedmetadataEvent should happen if
+        // we go directly from below HAVE_METADATA to higher than
+        // HAVE_METADATA.
+        if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
+            prepareMediaFragmentURI();
+            scheduleEvent(eventNames().durationchangeEvent);
+            scheduleResizeEvent();
+            scheduleEvent(eventNames().loadedmetadataEvent);
</ins><span class="cx"> #if ENABLE(WIRELESS_PLAYBACK_TARGET)
</span><del>-        if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent))
-            enqueuePlaybackTargetAvailabilityChangedEvent();
</del><ins>+            if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent))
+                enqueuePlaybackTargetAvailabilityChangedEvent();
</ins><span class="cx"> #endif
</span><del>-        m_initiallyMuted = m_volume < 0.05 || muted();
</del><ins>+            m_initiallyMuted = m_volume < 0.05 || muted();
</ins><span class="cx"> 
</span><del>-        updateRenderer();
</del><ins>+            updateRenderer();
</ins><span class="cx"> 
</span><del>-        if (is<MediaDocument>(document()))
-            downcast<MediaDocument>(document()).mediaElementNaturalSizeChanged(expandedIntSize(m_player->naturalSize()));
</del><ins>+            if (is<MediaDocument>(document()))
+                downcast<MediaDocument>(document()).mediaElementNaturalSizeChanged(expandedIntSize(m_player->naturalSize()));
</ins><span class="cx"> 
</span><del>-        logMediaLoadRequest(document().page(), m_player->engineDescription(), String(), true);
</del><ins>+            logMediaLoadRequest(document().page(), m_player->engineDescription(), String(), true);
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(WIRELESS_PLAYBACK_TARGET)
</span><del>-        scheduleUpdateMediaState();
</del><ins>+            scheduleUpdateMediaState();
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-        mediaSession().clientCharacteristicsChanged();
-    }
</del><ins>+            mediaSession().clientCharacteristicsChanged();
</ins><span class="cx"> 
</span><del>-    if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA) {
-        if (!m_haveFiredLoadedData) {
-            m_haveFiredLoadedData = true;
-            scheduleEvent(eventNames().loadeddataEvent);
-            // FIXME: It's not clear that it's correct to skip these this operation just
-            // because m_haveFiredLoadedData is already true. At one time we were skipping
-            // the call to setShouldDelayLoadEvent, which was definitely incorrect.
-            applyMediaFragmentURI();
</del><ins>+            // As the spec only mentiones HAVE_METADATA, run the later
+            // steps if we are moving to a higher state.
+            if (m_readyState == HAVE_METADATA)
+                break;
</ins><span class="cx">         }
</span><del>-        setShouldDelayLoadEvent(false);
-    }
</del><span class="cx"> 
</span><del>-    if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady)
-        scheduleEvent(eventNames().canplayEvent);
</del><ins>+        if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA) {
+            if (!m_haveFiredLoadedData) {
+                m_haveFiredLoadedData = true;
+                scheduleEvent(eventNames().loadeddataEvent);
+                // FIXME: It's not clear that it's correct to skip these this operation just
+                // because m_haveFiredLoadedData is already true. At one time we were skipping
+                // the call to setShouldDelayLoadEvent, which was definitely incorrect.
+                applyMediaFragmentURI();
+            }
+            setShouldDelayLoadEvent(false);
</ins><span class="cx"> 
</span><del>-    if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
-        if (oldState <= HAVE_CURRENT_DATA)
</del><ins>+            // If the new ready state is HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, then the relevant steps below must then be run also.
+            if (m_readyState < HAVE_FUTURE_DATA)
+                break;
+        }
+
+        if (!tracksAreReady)
+            break;
+
+        if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) {
</ins><span class="cx">             scheduleEvent(eventNames().canplayEvent);
</span><span class="cx"> 
</span><del>-        scheduleEvent(eventNames().canplaythroughEvent);
</del><ins>+            // If the element’s paused attribute is false, the user agent must queue a task to fire a simple event named playing at the element.
+            if (!paused())
+                scheduleNotifyAboutPlaying();
+            break;
+        }
</ins><span class="cx"> 
</span><del>-        auto canTransition = canTransitionFromAutoplayToPlay();
-        if (canTransition) {
-            m_paused = false;
-            setShowPosterFlag(false);
-            invalidateCachedTime();
-            setAutoplayEventPlaybackState(AutoplayEventPlaybackState::StartedWithoutUserGesture);
-            m_playbackStartedTime = currentMediaTime().toDouble();
-            scheduleEvent(eventNames().playEvent);
-        } else if (canTransition.error() == MediaPlaybackDenialReason::UserGestureRequired) {
-            ALWAYS_LOG(LOGIDENTIFIER, "Autoplay blocked, user gesture required");
-            setAutoplayEventPlaybackState(AutoplayEventPlaybackState::PreventedAutoplay);
</del><ins>+        if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) {
+            // If the previous ready state was HAVE_CURRENT_DATA or less,
+            // the user agent must queue a media element task given the media element to fire an event named canplay at the element,
+            // and, if the element's paused attribute is false, notify about playing for the element.
+            if (oldState <= HAVE_CURRENT_DATA) {
+                scheduleEvent(eventNames().canplayEvent);
+                if (!paused())
+                    scheduleNotifyAboutPlaying();
+            }
+
+            // The user agent must queue a media element task given the media element to fire an event named canplaythrough at the element.
+            scheduleEvent(eventNames().canplaythroughEvent);
+
+            // If the element is not eligible for autoplay, then the user agent must abort these substeps.
+            // The user agent may run the following substeps:
+            // Set the paused attribute to false.
+            // If the element's show poster flag is true, set it to false and run the time marches on steps.
+            // Queue a media element task given the element to fire an event named play at the element.
+            // Notify about playing for the element.
+            auto canTransition = canTransitionFromAutoplayToPlay();
+            if (canTransition) {
+                m_paused = false;
+                setShowPosterFlag(false);
+                invalidateCachedTime();
+                setAutoplayEventPlaybackState(AutoplayEventPlaybackState::StartedWithoutUserGesture);
+                m_playbackStartedTime = currentMediaTime().toDouble();
+                scheduleEvent(eventNames().playEvent);
+                scheduleNotifyAboutPlaying();
+            } else if (canTransition.error() == MediaPlaybackDenialReason::UserGestureRequired) {
+                ALWAYS_LOG(LOGIDENTIFIER, "Autoplay blocked, user gesture required");
+                setAutoplayEventPlaybackState(AutoplayEventPlaybackState::PreventedAutoplay);
+            }
</ins><span class="cx">         }
</span><del>-    }
</del><ins>+    } while (false);
</ins><span class="cx"> 
</span><span class="cx">     // If we transition to the Future Data state and we're about to begin playing, ensure playback is actually permitted first,
</span><span class="cx">     // honoring any playback denial reasons such as the requirement of a user gesture.
</span><span class="lines">@@ -3545,8 +3585,14 @@
</span><span class="cx"> 
</span><span class="cx">         scheduleEvent(eventNames().playEvent);
</span><span class="cx"> 
</span><ins>+        // If the media element's readyState attribute has the value HAVE_NOTHING, HAVE_METADATA, or HAVE_CURRENT_DATA,
+        // queue a media element task given the media element to fire an event named waiting at the element.
+        // Otherwise, the media element's readyState attribute has the value HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA:
+        // notify about playing for the element.
</ins><span class="cx">         if (m_readyState <= HAVE_CURRENT_DATA)
</span><span class="cx">             scheduleEvent(eventNames().waitingEvent);
</span><ins>+        else
+            scheduleNotifyAboutPlaying();
</ins><span class="cx">     } else if (m_readyState >= HAVE_FUTURE_DATA)
</span><span class="cx">         scheduleResolvePendingPlayPromises();
</span><span class="cx"> 
</span><span class="lines">@@ -5482,9 +5528,6 @@
</span><span class="cx"> 
</span><span class="cx">     m_playing = playing;
</span><span class="cx"> 
</span><del>-    if (m_playing)
-        scheduleNotifyAboutPlaying();
-
</del><span class="cx">     document().updateIsPlayingMedia();
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(WIRELESS_PLAYBACK_TARGET)
</span></span></pre>
</div>
</div>

</body>
</html>