Skip to content

Commit 606b5bc

Browse files
committed
Bug 1707584 - part3 : implement :buffering and :stalled pseudo classes. r=media-playback-reviewers,firefox-style-system-reviewers,emilio,chunmin
In this patch, we implement :buffering [1] and :stalled [2], but we are still unable to pass the corresponding WPTs. Those tests can't make Firefox dispatch `stalled` event properly and requires additional changes. In addition, the spec currently treats `:stalled` as a subset of `:buffering`, which may be incorrect. We need further clarification on this behavior in the spec. [1] https://html.spec.whatwg.org/multipage/semantics-other.html#selector-buffering [2] https://html.spec.whatwg.org/multipage/semantics-other.html#selector-stalled Differential Revision: https://phabricator.services.mozilla.com/D281039
1 parent adc2545 commit 606b5bc

File tree

8 files changed

+51
-15
lines changed

8 files changed

+51
-15
lines changed

dom/base/rust/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use bitflags::bitflags;
88
use malloc_size_of::malloc_size_of_is_0;
99

10-
pub const HEADING_LEVEL_OFFSET: usize = 54;
10+
pub const HEADING_LEVEL_OFFSET: usize = 56;
1111

1212
bitflags! {
1313
/// Event-based element states.
@@ -148,6 +148,10 @@ bitflags! {
148148
const PAUSED = 1u64 << 52;
149149
/// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-seeking
150150
const SEEKING = 1u64 << 53;
151+
/// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-buffering
152+
const BUFFERING = 1u64 << 54;
153+
/// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-stalled
154+
const STALLED = 1u64 << 55;
151155
/// https://drafts.csswg.org/selectors-5/#headings
152156
/// These 4 bits are used to pack the elements heading level into the element state
153157
/// Heading levels can be from 1-9 so 4 bits allows us to express the full range.

dom/media/mediaelement/HTMLMediaElement.cpp

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4739,6 +4739,8 @@ void HTMLMediaElement::Init() {
47394739
mWatchManager.Watch(mTracksCaptured,
47404740
&HTMLMediaElement::UpdateOutputTrackSources);
47414741
mWatchManager.Watch(mReadyState, &HTMLMediaElement::UpdateOutputTrackSources);
4742+
mWatchManager.Watch(mReadyState,
4743+
&HTMLMediaElement::UpdatePlaybackPseudoClasses);
47424744

47434745
mWatchManager.Watch(mDownloadSuspendedByCache,
47444746
&HTMLMediaElement::UpdateReadyStateInternal);
@@ -5112,13 +5114,27 @@ void HTMLMediaElement::UpdateWakeLock() {
51125114

51135115
void HTMLMediaElement::UpdatePlaybackPseudoClasses() {
51145116
MOZ_ASSERT(NS_IsMainThread());
5117+
LOG(LogLevel::Debug,
5118+
("%p UpdatePlaybackPseudoClasses: mPaused=%d, mNetworkState=%d, "
5119+
"mReadyState=%d, mIsCurrentlyStalled=%d",
5120+
this, mPaused.Ref(), mNetworkState, mReadyState.Ref(),
5121+
mIsCurrentlyStalled));
5122+
AutoStateChangeNotifier notifier(*this, /*aNotify=*/true);
5123+
RemoveStatesSilently(ElementState::PAUSED | ElementState::BUFFERING | ElementState::STALLED);
5124+
// We don’t need to update the playing state because these states are
5125+
// exclusive, and the `:playing` pseudo-class is determined by checking
5126+
// the element's PAUSED state.
51155127
if (mPaused) {
5116-
// We don’t need to update the playing state because these states are
5117-
// exclusive, and the `:playing` pseudo-class is determined by checking
5118-
// the element's PAUSED state.
5119-
AddStates(ElementState::PAUSED);
5120-
} else {
5121-
RemoveStates(ElementState::PAUSED);
5128+
AddStatesSilently(ElementState::PAUSED);
5129+
return;
5130+
}
5131+
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-buffering
5132+
if (mNetworkState == NETWORK_LOADING && mReadyState <= HAVE_CURRENT_DATA) {
5133+
AddStatesSilently(ElementState::BUFFERING);
5134+
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-stalled
5135+
if (mIsCurrentlyStalled) {
5136+
AddStatesSilently(ElementState::STALLED);
5137+
}
51225138
}
51235139
}
51245140

@@ -6224,6 +6240,8 @@ void HTMLMediaElement::NotifySuspendedByCache(bool aSuspendedByCache) {
62246240

62256241
void HTMLMediaElement::DownloadSuspended() {
62266242
if (mNetworkState == NETWORK_LOADING) {
6243+
mIsCurrentlyStalled = false;
6244+
UpdatePlaybackPseudoClasses();
62276245
QueueEvent(u"progress"_ns);
62286246
}
62296247
ChangeNetworkState(NETWORK_IDLE);
@@ -6254,6 +6272,8 @@ void HTMLMediaElement::CheckProgress(bool aHaveNewProgress) {
62546272
: (now - mProgressTime >=
62556273
TimeDuration::FromMilliseconds(PROGRESS_MS) &&
62566274
mDataTime > mProgressTime)) {
6275+
mIsCurrentlyStalled = false;
6276+
UpdatePlaybackPseudoClasses();
62576277
QueueEvent(u"progress"_ns);
62586278
// Going back 1ms ensures that future data will have now > mProgressTime,
62596279
// and so will trigger another event. mDataTime is not reset because it
@@ -6279,6 +6299,8 @@ void HTMLMediaElement::CheckProgress(bool aHaveNewProgress) {
62796299

62806300
if (now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
62816301
if (!mMediaSource) {
6302+
mIsCurrentlyStalled = true;
6303+
UpdatePlaybackPseudoClasses();
62826304
QueueEvent(u"stalled"_ns);
62836305
} else {
62846306
ChangeDelayLoadStatus(false);
@@ -6631,6 +6653,7 @@ void HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState) {
66316653

66326654
nsMediaNetworkState oldState = mNetworkState;
66336655
mNetworkState = aState;
6656+
UpdatePlaybackPseudoClasses();
66346657
LOG(LogLevel::Debug,
66356658
("%p Network state changed to %s", this, gNetworkStateToString[aState]));
66366659
DDLOG(DDLogCategory::Property, "network_state",

dom/media/mediaelement/HTMLMediaElement.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,9 @@ class HTMLMediaElement : public nsGenericHTMLElement,
18951895
// For use by mochitests. Enabling pref "media.test.video-suspend"
18961896
bool mForcedHidden = false;
18971897

1898+
// https://html.spec.whatwg.org/multipage/media.html#is-currently-stalled
1899+
bool mIsCurrentlyStalled = false;
1900+
18981901
Visibility mVisibilityState = Visibility::Untracked;
18991902

19001903
UniquePtr<ErrorSink> mErrorSink;

servo/components/style/gecko/non_ts_pseudo_class_list.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ macro_rules! apply_non_ts_list {
9696
("paused", Paused, PAUSED, _),
9797
("playing", Playing, PAUSED, _),
9898
("seeking", Seeking, SEEKING, _),
99+
("buffering", Buffering, BUFFERING, _),
100+
("stalled", Stalled, STALLED, _),
99101

100102

101103
// NOTE(emilio): Pseudo-classes below only depend on document state, and thus

servo/components/style/gecko/selector_parser.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,10 @@ impl NonTSPseudoClass {
227227
if matches!(*self, Self::Heading(..)) {
228228
return static_prefs::pref!("layout.css.heading-selector.enabled");
229229
}
230-
if matches!(*self, Self::Playing | Self::Paused | Self::Seeking) {
230+
if matches!(
231+
*self,
232+
Self::Playing | Self::Paused | Self::Seeking | Self::Buffering | Self::Stalled
233+
) {
231234
return static_prefs::pref!("dom.media.pseudo-classes.enabled");
232235
}
233236
!self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)

servo/components/style/gecko/wrapper.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2086,7 +2086,9 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
20862086
| NonTSPseudoClass::ActiveViewTransition
20872087
| NonTSPseudoClass::MozValueEmpty
20882088
| NonTSPseudoClass::MozSuppressForPrintSelection
2089-
| NonTSPseudoClass::Seeking => {
2089+
| NonTSPseudoClass::Seeking
2090+
| NonTSPseudoClass::Buffering
2091+
| NonTSPseudoClass::Stalled => {
20902092
self.state().intersects(pseudo_class.state_flag())
20912093
},
20922094
NonTSPseudoClass::Paused => self.is_html_media_element() && self.state().intersects(ElementState::PAUSED),
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
[media-loading-state-timing.sub.html]
2+
expected: TIMEOUT
23
[Test :stalled timing relative to stalled and progress events]
3-
expected: FAIL
4+
expected: TIMEOUT
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
[media-loading-state.html]
2-
[Test :pseudo-class syntax is supported without throwing a SyntaxError]
3-
expected: FAIL
4-
2+
expected: TIMEOUT
53
[Test :stalled pseudo-class]
6-
expected: FAIL
4+
expected: TIMEOUT
75

86
[Test :buffering pseudo-class]
9-
expected: FAIL
7+
expected: NOTRUN

0 commit comments

Comments
 (0)