8000 [:has() pseudo-class] Support invalidation for :buffering and :stalle… · WebKit/WebKit@2974b49 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2974b49

Browse files
committed
[:has() pseudo-class] Support invalidation for :buffering and :stalled pseudo-classes
https://bugs.webkit.org/show_bug.cgi?id=249455 rdar://103438119 Reviewed by Antti Koivisto. * LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/media-loading-pseudo-classes-in-has-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/media-loading-pseudo-classes-in-has.html: Added. * LayoutTests/platform/gtk/imported/w3c/web-platform-tests/css/selectors/invalidation/media-loading-pseudo-classes-in-has-expected.txt: Added. * Source/WebCore/html/HTMLMediaElement.cpp: (WebCore::HTMLMediaElement::parseAttribute): Remove `invalidateStyle()` call since we already switched it to PseudoClassChangeInvalidation in 257991@main (WebCore::HTMLMediaElement::setNetworkState): (WebCore::HTMLMediaElement::setReadyState): (WebCore::HTMLMediaElement::progressEventTimerFired): (WebCore::HTMLMediaElement::setPaused): (WebCore::HTMLMediaElement::updateBufferingState): (WebCore::HTMLMediaElement::updateStalledState): (WebCore::HTMLMediaElement::buffering const): Deleted. (WebCore::HTMLMediaElement::stalled const): Deleted. * Source/WebCore/html/HTMLMediaElement.h: (WebCore::HTMLMediaElement::buffering const): (WebCore::HTMLMediaElement::stalled const): Canonical link: https://commits.webkit.org/258891@main
1 parent aceba59 commit 2974b49

File tree

5 files changed

+121
-17
lines changed
  • platform/gtk/imported/w3c/web-platform-tests/css/selectors/invalidation
  • Source/WebCore/html
  • 5 files changed

    +121
    -17
    lines changed
    Lines changed: 5 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,5 @@
    1+
    2+
    3+
    PASS Test :has(:stalled) invalidation
    4+
    PASS Test :has(:buffering) invalidation
    5+
    Lines changed: 79 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,79 @@
    1+
    <!DOCTYPE html>
    2+
    <meta name="timeout" content="long" />
    3+
    <title>:has() invalidation with :buffering & :stalled pseudo-classes</title>
    4+
    <link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
    5+
    <link rel="help" href="https://drafts.csswg.org/selectors/#relational">
    6+
    <link rel="help" href="https://w3c.github.io/csswg-drafts/selectors/#media-loading-state">
    7+
    8+
    <style>
    9+
    #subject:has(video:buffering) {
    10+
    background-color: blue;
    11+
    }
    12+
    #subject:has(video:stalled) {
    13+
    border-color: green;
    14+
    }
    15+
    </style>
    16+
    17+
    <div id="subject">
    18+
    <video width="300" height="300" muted loop controls></video>
    19+
    </div>
    20+
    21+
    <script src="/resources/testharness.js"></script>
    22+
    <script src="/resources/testharnessreport.js"></script>
    23+
    <script>
    24+
    const BLUE = "rgb(0, 0, 255)";
    25+
    const GREEN = "rgb(0, 128, 0)";
    26+
    27+
    function assert_buffering_state(buffering) {
    28+
    if (buffering)
    29+
    assert_equals(getComputedStyle(subject).backgroundColor, BLUE);
    30+
    else
    31+
    assert_not_equals(getComputedStyle(subject).backgroundColor, BLUE);
    32+
    }
    33+
    34+
    function assert_stalled_state(stalled) {
    35+
    if (stalled)
    36+
    assert_equals(getComputedStyle(subject).borderColor, GREEN);
    37+
    else
    38+
    assert_not_equals(getComputedStyle(subject).borderColor, GREEN);
    39+
    }
    40+
    41+
    promise_test(async (t) => {
    42+
    const video = document.querySelector("video");
    43+
    assert_stalled_state(false);
    44+
    await new Promise((r) => {
    45+
    video.addEventListener("stalled", r, { once: true });
    46+
    video.src = `/media/counting.mp4?pipe=trickle(100:d1:r2)&random=${Math.random()}`;
    47+
    });
    48+
    assert_stalled_state(false);
    49+
    const promise = video.play();
    50+
    assert_stalled_state(true);
    51+
    video.src = "";
    52+
    // Wait for the video to abort trying to play
    53+
    try {
    54+
    await promise;
    55+
    } catch (err) {}
    56+
    await video.pause();
    57+
    assert_stalled_state(false);
    58+
    }, "Test :has(:stalled) invalidation");
    59+
    60+
    promise_test(async (t) => {
    61+
    const video = document.querySelector("video");
    62+
    assert_buffering_state(false);
    63+
    await new Promise((r) => {
    64+
    video.addEventListener("stalled", r, { once: true });
    65+
    video.src = `/media/counting.mp4?pipe=trickle(100:d1:r2)&random=${Math.random()}`;
    66+
    });
    67+
    video.currentTime = 10;
    68+
    assert_buffering_state(false);
    69+
    const promise = video.play();
    70+
    assert_buffering_state(true);
    71+
    video.src = "";
    72+
    // Wait for the video to abort trying to play
    73+
    try {
    74+
    await promise;
    75+
    } catch (err) {}
    76+
    await video.pause();
    77+
    assert_buffering_state(false);
    78+
    }, "Test :has(:buffering) invalidation");
    79+
    </script>
    Lines changed: 7 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,7 @@
    1+
    2+
    3+
    Harness Error (TIMEOUT), message = null
    4+
    5+
    TIMEOUT Test :has(:stalled) invalidation Test timed out
    6+
    NOTRUN Test :has(:buffering) invalidation
    7+

    Source/WebCore/html/HTMLMediaElement.cpp

    Lines changed: 22 additions & 15 deletions
    Original file line numberDiff line numberDiff line change
    @@ -828,10 +828,6 @@ void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomStrin
    828828
    }
    829829
    else
    830830
    HTMLElement::parseAttribute(name, value);
    831-
    832-
    // Changing the "muted" attribue could affect ":muted"
    833-
    if (name == mutedAttr)
    834-
    invalidateStyle();
    835831
    }
    836832

    837833
    void HTMLMediaElement::finishParsingChildren()
    @@ -2605,7 +2601,8 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
    26052601
    if (state == MediaPlayer::NetworkState::Empty) {
    26062602
    // Just update the cached state and leave, we can't do anything.
    26072603
    m_networkState = NETWORK_EMPTY;
    2608-
    invalidateStyle();
    2604+
    updateBufferingState();
    2605+
    updateStalledState();
    26092606
    return;
    26102607
    }
    26112608

    @@ -2635,7 +2632,8 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
    26352632
    m_completelyLoaded = true;
    26362633
    }
    26372634

    2638-
    invalidateStyle();
    2635+
    updateBufferingState();
    2636+
    updateStalledState();
    26392637
    }
    26402638

    26412639
    void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
    @@ -2918,7 +2916,8 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
    29182916
    updateMediaController();
    29192917
    updateActiveTextTrackCues(currentMediaTime());
    29202918

    2921-
    invalidateStyle();
    2919+
    updateBufferingState();
    2920+
    updateStalledState();
    29222921
    }
    29232922

    29242923
    #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
    @@ -3270,13 +3269,13 @@ void HTMLMediaElement::progressEventTimerFired()
    32703269
    m_previousProgressTime = time;
    32713270
    if (m_sentStalledEvent) {
    32723271
    m_sentStalledEvent = false;
    3273-
    invalidateStyle();
    3272+
    updateStalledState();
    32743273
    }
    32753274
    updateRenderer();
    32763275
    } else if (timedelta > 3_s && !m_sentStalledEvent) {
    32773276
    scheduleEvent(eventNames().stalledEvent);
    32783277
    m_sentStalledEvent = true;
    3279-
    invalidateStyle();
    3278+
    updateStalledState();
    32803279
    setShouldDelayLoadEvent(false);
    32813280
    }
    32823281
    });
    @@ -3796,8 +3795,8 @@ void HTMLMediaElement::setPaused(bool paused)
    37963795
    { CSSSelector::PseudoClassPlaying, !paused },
    37973796
    });
    37983797
    m_paused = paused;
    3799-
    // FIXME: Use PseudoClassChangeInvalidation for :buffering/:stalling and remove the line below.
    3800-
    invalidateStyle();
    3798+
    updateBufferingState();
    3799+
    updateStalledState();
    38013800
    }
    38023801

    38033802
    double HTMLMediaElement::defaultPlaybackRate() const
    @@ -4280,7 +4279,7 @@ void HTMLMediaElement::setVolumeLocked(bool locked)
    42804279
    m_volumeLocked = locked;
    42814280
    }
    42824281

    4283-
    bool HTMLMediaElement::buffering() const
    4282+
    void HTMLMediaElement::updateBufferingState()
    42844283
    {
    42854284
    // CSS Selectors Level 4; Editor's Draft, 2 July 2021
    42864285
    // <https://drafts.csswg.org/selectors/>
    @@ -4291,10 +4290,14 @@ bool HTMLMediaElement::buffering() const
    42914290
    // but has not yet obtained enough data to resume playback. (Note that the element is still considered
    42924291
    // to be “playing” when it is “buffering”. Whenever :buffering matches an element, :playing also
    42934292
    // matches the element.)
    4294-
    return !paused() && m_networkState == NETWORK_LOADING && m_readyState <= HAVE_CURRENT_DATA;
    4293+
    bool buffering = !paused() && m_networkState == NETWORK_LOADING && m_readyState <= HAVE_CURRENT_DATA;
    4294+
    if (m_buffering != buffering) {
    4295+
    Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassBuffering, buffering);
    4296+
    m_buffering = buffering;
    4297+
    }
    42954298
    }
    42964299

    4297-
    bool HTMLMediaElement::stalled() const
    4300+
    void HTMLMediaElement::updateStalledState()
    42984301
    {
    42994302
    // CSS Selectors Level 4; Editor's Draft, 2 July 2021
    43004303
    // <https://drafts.csswg.org/selectors/>
    @@ -4306,7 +4309,11 @@ bool HTMLMediaElement::stalled() const
    43064309
    // stall timeout. [HTML] (Note that, like with the :buffering pseudo-class, the element is still
    43074310
    // considered to be “playing” when it is “stalled”. Whenever :stalled matches an element, :playing
    43084311
    // also matches the element.)
    4309-
    return !paused() && m_networkState == NETWORK_LOADING && m_readyState <= HAVE_CURRENT_DATA && m_sentStalledEvent;
    4312+
    bool stalled = !paused() && m_networkState == NETWORK_LOADING && m_readyState <= HAVE_CURRENT_DATA && m_sentStalledEvent;
    4313+
    if (m_stalled != stalled) {
    4314+
    Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassStalled, stalled);
    4315+
    m_stalled = stalled;
    4316+
    }
    43104317
    }
    43114318

    43124319
    #if USE(AUDIO_SESSION) && PLATFORM(MAC)

    Source/WebCore/html/HTMLMediaElement.h

    Lines changed: 8 additions & 2 deletions
    Original file line numberDiff line numberDiff line change
    @@ -336,8 +336,12 @@ class HTMLMediaElement
    336336

    337337
    bool volumeLocked() const { return m_volumeLocked; }
    338338
    WEBCORE_EXPORT void setVolumeLocked(bool);
    339-
    bool buffering() const;
    340-
    bool stalled() const;
    339+
    340+
    bool buffering() const { return m_buffering; }
    341+
    void updateBufferingState();
    342+
    343+
    bool stalled() const { return m_stalled; }
    344+
    void updateStalledState();
    341345

    342346
    WEBCORE_EXPORT void togglePlayState();
    343347
    WEBCORE_EXPORT void beginScrubbing() override;
    @@ -1149,6 +1153,8 @@ class HTMLMediaElement
    11491153
    bool m_initiallyMuted : 1;
    11501154
    bool m_paused : 1;
    11511155
    bool m_seeking : 1;
    1156+
    bool m_buffering : 1;
    1157+
    bool m_stalled : 1;
    11521158
    bool m_seekRequested : 1;
    11531159
    bool m_wasPlayingBeforeSeeking : 1;
    11541160

    0 commit comments

    Comments
     (0)
    0