Diff
Modified: trunk/LayoutTests/ChangeLog (281502 => 281503)
--- trunk/LayoutTests/ChangeLog 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/LayoutTests/ChangeLog 2021-08-24 18:30:02 UTC (rev 281503)
@@ -1,3 +1,17 @@
+2021-08-24 Jer Noble <[email protected]>
+
+ Add support for new pseudo-selectors for media from CSS Selectors Level 4.
+ https://bugs.webkit.org/show_bug.cgi?id=229431
+
+ Reviewed by Eric Carlson.
+
+ * media/media-css-muted-expected.txt: Added.
+ * media/media-css-muted.html: Added.
+ * media/media-css-playing-paused-expected.txt: Added.
+ * media/media-css-playing-paused.html: Added.
+ * media/media-css-volume-locked-expected.txt: Added.
+ * media/media-css-volume-locked.html: Added.
+
2021-08-24 Eric Hutchison <[email protected]>
[BigSur wk2 Debug x86] http/tests/inspector/network/resource-timing.html is a flaky failure.
Added: trunk/LayoutTests/media/media-css-muted-expected.txt (0 => 281503)
--- trunk/LayoutTests/media/media-css-muted-expected.txt (rev 0)
+++ trunk/LayoutTests/media/media-css-muted-expected.txt 2021-08-24 18:30:02 UTC (rev 281503)
@@ -0,0 +1,11 @@
+
+RUN(video.src = "" "content/test"))
+EVENT(canplay)
+EXPECTED (video.muted == 'false') OK
+EXPECTED (document.querySelector("video:muted") == 'null') OK
+EXPECTED (document.querySelector("video:not(:muted)") == '[object HTMLVideoElement]') OK
+RUN(video.muted = true)
+EXPECTED (document.querySelector("video:muted") == '[object HTMLVideoElement]') OK
+EXPECTED (document.querySelector("video:not(:muted)") == 'null') OK
+END OF TEST
+
Added: trunk/LayoutTests/media/media-css-muted.html (0 => 281503)
--- trunk/LayoutTests/media/media-css-muted.html (rev 0)
+++ trunk/LayoutTests/media/media-css-muted.html 2021-08-24 18:30:02 UTC (rev 281503)
@@ -0,0 +1,25 @@
+<!doctype HTML>
+<html>
+<head>
+ <title>media-css-muted</title>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ findMediaElement();
+ run('video.src = "" "content/test")');
+ await waitFor(video, 'canplay');
+ testExpected('video.muted', false);
+ testExpected('document.querySelector("video:muted")', null);
+ testExpected('document.querySelector("video:not(:muted)")', video);
+ run('video.muted = true');
+ testExpected('document.querySelector("video:muted")', video);
+ testExpected('document.querySelector("video:not(:muted)")', null);
+ endTest();
+ });
+ </script>
+<head>
+<body>
+ <video></video>
+</body>
+</html>
Added: trunk/LayoutTests/media/media-css-playing-paused-expected.txt (0 => 281503)
--- trunk/LayoutTests/media/media-css-playing-paused-expected.txt (rev 0)
+++ trunk/LayoutTests/media/media-css-playing-paused-expected.txt 2021-08-24 18:30:02 UTC (rev 281503)
@@ -0,0 +1,16 @@
+
+RUN(video.src = "" "content/test"))
+EVENT(canplay)
+EXPECTED (video.paused == 'true') OK
+EXPECTED (document.querySelector("video:playing") == 'null') OK
+EXPECTED (document.querySelector("video:not(:playing)") == '[object HTMLVideoElement]') OK
+EXPECTED (document.querySelector("video:paused") == '[object HTMLVideoElement]') OK
+EXPECTED (document.querySelector("video:not(:paused)") == 'null') OK
+RUN(video.play())
+EVENT(playing)
+EXPECTED (document.querySelector("video:playing") == '[object HTMLVideoElement]') OK
+EXPECTED (document.querySelector("video:not(:playing)") == 'null') OK
+EXPECTED (document.querySelector("video:paused") == 'null') OK
+EXPECTED (document.querySelector("video:not(:paused)") == '[object HTMLVideoElement]') OK
+END OF TEST
+
Added: trunk/LayoutTests/media/media-css-playing-paused.html (0 => 281503)
--- trunk/LayoutTests/media/media-css-playing-paused.html (rev 0)
+++ trunk/LayoutTests/media/media-css-playing-paused.html 2021-08-24 18:30:02 UTC (rev 281503)
@@ -0,0 +1,30 @@
+<!doctype HTML>
+<html>
+<head>
+ <title>media-css-playing-paused</title>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ findMediaElement();
+ run('video.src = "" "content/test")');
+ await waitFor(video, 'canplay');
+ testExpected('video.paused', true);
+ testExpected('document.querySelector("video:playing")', null);
+ testExpected('document.querySelector("video:not(:playing)")', video);
+ testExpected('document.querySelector("video:paused")', video);
+ testExpected('document.querySelector("video:not(:paused)")', null);
+ run('video.play()');
+ await waitFor(video, 'playing');
+ testExpected('document.querySelector("video:playing")', video);
+ testExpected('document.querySelector("video:not(:playing)")', null);
+ testExpected('document.querySelector("video:paused")', null);
+ testExpected('document.querySelector("video:not(:paused)")', video);
+ endTest();
+ });
+ </script>
+<head>
+<body>
+ <video loop></video>
+</body>
+</html>
Added: trunk/LayoutTests/media/media-css-volume-locked-expected.txt (0 => 281503)
--- trunk/LayoutTests/media/media-css-volume-locked-expected.txt (rev 0)
+++ trunk/LayoutTests/media/media-css-volume-locked-expected.txt 2021-08-24 18:30:02 UTC (rev 281503)
@@ -0,0 +1,11 @@
+
+RUN(video.src = "" "content/test"))
+EVENT(canplay)
+RUN(internals.setMediaElementVolumeLocked(video, false))
+EXPECTED (document.querySelector("video:volume-locked") == 'null') OK
+EXPECTED (document.querySelector("video:not(:volume-locked)") == '[object HTMLVideoElement]') OK
+RUN(internals.setMediaElementVolumeLocked(video, true))
+EXPECTED (document.querySelector("video:volume-locked") == '[object HTMLVideoElement]') OK
+EXPECTED (document.querySelector("video:not(:volume-locked)") == 'null') OK
+END OF TEST
+
Added: trunk/LayoutTests/media/media-css-volume-locked.html (0 => 281503)
--- trunk/LayoutTests/media/media-css-volume-locked.html (rev 0)
+++ trunk/LayoutTests/media/media-css-volume-locked.html 2021-08-24 18:30:02 UTC (rev 281503)
@@ -0,0 +1,25 @@
+<!doctype HTML>
+<html>
+<head>
+ <title>media-css-muted</title>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ findMediaElement();
+ run('video.src = "" "content/test")');
+ await waitFor(video, 'canplay');
+ run('internals.setMediaElementVolumeLocked(video, false)');
+ testExpected('document.querySelector("video:volume-locked")', null);
+ testExpected('document.querySelector("video:not(:volume-locked)")', video);
+ run('internals.setMediaElementVolumeLocked(video, true)');
+ testExpected('document.querySelector("video:volume-locked")', video);
+ testExpected('document.querySelector("video:not(:volume-locked)")', null);
+ endTest();
+ });
+ </script>
+<head>
+<body>
+ <video></video>
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (281502 => 281503)
--- trunk/Source/WebCore/ChangeLog 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/ChangeLog 2021-08-24 18:30:02 UTC (rev 281503)
@@ -1,3 +1,68 @@
+2021-08-24 Jer Noble <[email protected]>
+
+ Add support for new pseudo-classes for media from CSS Selectors Level 4.
+ https://bugs.webkit.org/show_bug.cgi?id=229431
+
+ Reviewed by Eric Carlson.
+
+ Tests: media/media-css-muted.html
+ media/media-css-playing-paused.html
+ media/media-css-volume-locked.html
+ (Tests for buffering and stalled states are forthcoming, waiting on
+ a non-flakey mechanism for inducing network stalls)
+
+ Add support for the following new pseudo-classes from CSS Selectors Level 4:
+ - :playing, :paused, :seeking
+ - :buffering, :stalled
+ - :muted, :volume-locked
+
+ Ensure CSS styles are invalidated when playback state changes by encapsulating
+ modifications to HTMLMediaElement::m_paused into a setPaused() function.
+
+ Add methods to query for volumeLocked() on HTMLMediaElement, as well as a way
+ to override the value through Internals.
+
+ Invalidate styles when the "muted" attribute changes state.
+
+ * css/CSSSelector.cpp:
+ (WebCore::CSSSelector::selectorText const):
+ * css/CSSSelector.h:
+ * css/SelectorChecker.cpp:
+ (WebCore::SelectorChecker::checkOne const):
+ * css/SelectorCheckerTestFunctions.h:
+ (WebCore::matchesPlayingPseudoClass):
+ (WebCore::matchesPausedPseudoClass):
+ (WebCore::matchesSeekingPseudoClass):
+ (WebCore::matchesBufferingPseudoClass):
+ (WebCore::matchesStalledPseudoClass):
+ (WebCore::matchesMutedPseudoClass):
+ (WebCore::matchesVolumeLockedPseudoClass):
+ * css/SelectorPseudoClassAndCompatibilityElementMap.in:
+ * cssjit/SelectorCompiler.cpp:
+ (WebCore::SelectorCompiler::JSC_DEFINE_JIT_OPERATION):
+ (WebCore::SelectorCompiler::addPseudoClassType):
+ * html/HTMLMediaElement.cpp:
+ (WebCore::defaultVolumeLocked):
+ (WebCore::HTMLMediaElement::HTMLMediaElement):
+ (WebCore::HTMLMediaElement::parseAttribute):
+ (WebCore::HTMLMediaElement::prepareForLoad):
+ (WebCore::HTMLMediaElement::setNetworkState):
+ (WebCore::HTMLMediaElement::setReadyState):
+ (WebCore::HTMLMediaElement::setPaused):
+ (WebCore::HTMLMediaElement::playInternal):
+ (WebCore::HTMLMediaElement::pauseInternal):
+ (WebCore::HTMLMediaElement::setMuted):
+ (WebCore::HTMLMediaElement::setVolumeLocked):
+ (WebCore::HTMLMediaElement::buffering const):
+ (WebCore::HTMLMediaElement::stalled const):
+ (WebCore::HTMLMediaElement::mediaPlayerTimeChanged):
+ * html/HTMLMediaElement.h:
+ (WebCore::HTMLMediaElement::volumeLocked const):
+ * testing/Internals.cpp:
+ (WebCore::Internals::setMediaElementVolumeLocked):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
2021-08-24 Chris Dumez <[email protected]>
Fire a load event for <iframe src=""
Modified: trunk/Source/WebCore/css/CSSSelector.cpp (281502 => 281503)
--- trunk/Source/WebCore/css/CSSSelector.cpp 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/css/CSSSelector.cpp 2021-08-24 18:30:02 UTC (rev 281503)
@@ -507,6 +507,27 @@
case CSSSelector::PseudoClassFuture:
builder.append(":future");
break;
+ case CSSSelector::PseudoClassPlaying:
+ builder.append(":playing");
+ break;
+ case CSSSelector::PseudoClassPaused:
+ builder.append(":paused");
+ break;
+ case CSSSelector::PseudoClassSeeking:
+ builder.append(":seeking");
+ break;
+ case CSSSelector::PseudoClassBuffering:
+ builder.append(":buffering");
+ break;
+ case CSSSelector::PseudoClassStalled:
+ builder.append(":stalled");
+ break;
+ case CSSSelector::PseudoClassMuted:
+ builder.append(":muted");
+ break;
+ case CSSSelector::PseudoClassVolumeLocked:
+ builder.append(":volume-locked");
+ break;
#endif
case CSSSelector::PseudoClassHas:
builder.append(":has(");
Modified: trunk/Source/WebCore/css/CSSSelector.h (281502 => 281503)
--- trunk/Source/WebCore/css/CSSSelector.h 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/css/CSSSelector.h 2021-08-24 18:30:02 UTC (rev 281503)
@@ -166,6 +166,13 @@
#if ENABLE(VIDEO)
PseudoClassFuture,
PseudoClassPast,
+ PseudoClassPlaying,
+ PseudoClassPaused,
+ PseudoClassSeeking,
+ PseudoClassBuffering,
+ PseudoClassStalled,
+ PseudoClassMuted,
+ PseudoClassVolumeLocked,
#endif
#if ENABLE(CSS_SELECTORS_LEVEL4)
PseudoClassDir,
Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (281502 => 281503)
--- trunk/Source/WebCore/css/SelectorChecker.cpp 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp 2021-08-24 18:30:02 UTC (rev 281503)
@@ -1063,6 +1063,20 @@
return matchesFutureCuePseudoClass(element);
case CSSSelector::PseudoClassPast:
return matchesPastCuePseudoClass(element);
+ case CSSSelector::PseudoClassPlaying:
+ return matchesPlayingPseudoClass(element);
+ case CSSSelector::PseudoClassPaused:
+ return matchesPausedPseudoClass(element);
+ case CSSSelector::PseudoClassSeeking:
+ return matchesSeekingPseudoClass(element);
+ case CSSSelector::PseudoClassBuffering:
+ return matchesBufferingPseudoClass(element);
+ case CSSSelector::PseudoClassStalled:
+ return matchesStalledPseudoClass(element);
+ case CSSSelector::PseudoClassMuted:
+ return matchesMutedPseudoClass(element);
+ case CSSSelector::PseudoClassVolumeLocked:
+ return matchesVolumeLockedPseudoClass(element);
#endif
case CSSSelector::PseudoClassScope:
Modified: trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h (281502 => 281503)
--- trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/css/SelectorCheckerTestFunctions.h 2021-08-24 18:30:02 UTC (rev 281503)
@@ -47,6 +47,7 @@
#endif
#if ENABLE(VIDEO)
+#include "HTMLMediaElement.h"
#include "WebVTTElement.h"
#endif
@@ -442,6 +443,40 @@
return is<WebVTTElement>(element) && downcast<WebVTTElement>(element).isPastNode();
}
+ALWAYS_INLINE bool matchesPlayingPseudoClass(const Element& element)
+{
+ return is<HTMLMediaElement>(element) && !downcast<HTMLMediaElement>(element).paused();
+}
+
+ALWAYS_INLINE bool matchesPausedPseudoClass(const Element& element)
+{
+ return is<HTMLMediaElement>(element) && downcast<HTMLMediaElement>(element).paused();
+}
+
+ALWAYS_INLINE bool matchesSeekingPseudoClass(const Element& element)
+{
+ return is<HTMLMediaElement>(element) && downcast<HTMLMediaElement>(element).seeking();
+}
+
+ALWAYS_INLINE bool matchesBufferingPseudoClass(const Element& element)
+{
+ return is<HTMLMediaElement>(element) && downcast<HTMLMediaElement>(element).buffering();
+}
+
+ALWAYS_INLINE bool matchesStalledPseudoClass(const Element& element)
+{
+ return is<HTMLMediaElement>(element) && downcast<HTMLMediaElement>(element).stalled();
+}
+
+ALWAYS_INLINE bool matchesMutedPseudoClass(const Element& element)
+{
+ return is<HTMLMediaElement>(element) && downcast<HTMLMediaElement>(element).muted();
+}
+
+ALWAYS_INLINE bool matchesVolumeLockedPseudoClass(const Element& element)
+{
+ return is<HTMLMediaElement>(element) && downcast<HTMLMediaElement>(element).volumeLocked();
+}
#endif
ALWAYS_INLINE bool isFrameFocused(const Element& element)
Modified: trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in (281502 => 281503)
--- trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/css/SelectorPseudoClassAndCompatibilityElementMap.in 2021-08-24 18:30:02 UTC (rev 281503)
@@ -93,5 +93,12 @@
#if ENABLE(VIDEO)
future
past
+playing
+paused
+seeking
+buffering
+stalled
+muted
+volume-locked
#endif
Modified: trunk/Source/WebCore/cssjit/SelectorCompiler.cpp (281502 => 281503)
--- trunk/Source/WebCore/cssjit/SelectorCompiler.cpp 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/cssjit/SelectorCompiler.cpp 2021-08-24 18:30:02 UTC (rev 281503)
@@ -111,6 +111,13 @@
#if ENABLE(VIDEO)
static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesFutureCuePseudoClass, bool, (const Element&));
static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesPastCuePseudoClass, bool, (const Element&));
+static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesPlayingPseudoClass, bool, (const Element&));
+static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesPausedPseudoClass, bool, (const Element&));
+static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesSeekingPseudoClass, bool, (const Element&));
+static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesBufferingPseudoClass, bool, (const Element&));
+static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesStalledPseudoClass, bool, (const Element&));
+static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesMutedPseudoClass, bool, (const Element&));
+static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesVolumeLockedPseudoClass, bool, (const Element&));
#endif
#if ENABLE(ATTACHMENT_ELEMENT)
static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationHasAttachment, bool, (const Element&));
@@ -741,6 +748,41 @@
{
return matchesPastCuePseudoClass(element);
}
+
+JSC_DEFINE_JIT_OPERATION(operationMatchesPlayingPseudoClass, bool, (const Element& element))
+{
+ return matchesPlayingPseudoClass(element);
+}
+
+JSC_DEFINE_JIT_OPERATION(operationMatchesPausedPseudoClass, bool, (const Element& element))
+{
+ return matchesPausedPseudoClass(element);
+}
+
+JSC_DEFINE_JIT_OPERATION(operationMatchesSeekingPseudoClass, bool, (const Element& element))
+{
+ return matchesSeekingPseudoClass(element);
+}
+
+JSC_DEFINE_JIT_OPERATION(operationMatchesBufferingPseudoClass, bool, (const Element& element))
+{
+ return matchesBufferingPseudoClass(element);
+}
+
+JSC_DEFINE_JIT_OPERATION(operationMatchesStalledPseudoClass, bool, (const Element& element))
+{
+ return matchesStalledPseudoClass(element);
+}
+
+JSC_DEFINE_JIT_OPERATION(operationMatchesMutedPseudoClass, bool, (const Element& element))
+{
+ return matchesMutedPseudoClass(element);
+}
+
+JSC_DEFINE_JIT_OPERATION(operationMatchesVolumeLockedPseudoClass, bool, (const Element& element))
+{
+ return matchesVolumeLockedPseudoClass(element);
+}
#endif
#if ENABLE(ATTACHMENT_ELEMENT)
@@ -867,6 +909,27 @@
case CSSSelector::PseudoClassPast:
fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesPastCuePseudoClass));
return FunctionType::SimpleSelectorChecker;
+ case CSSSelector::PseudoClassPlaying:
+ fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesPlayingPseudoClass));
+ return FunctionType::SimpleSelectorChecker;
+ case CSSSelector::PseudoClassPaused:
+ fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesPausedPseudoClass));
+ return FunctionType::SimpleSelectorChecker;
+ case CSSSelector::PseudoClassSeeking:
+ fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesSeekingPseudoClass));
+ return FunctionType::SimpleSelectorChecker;
+ case CSSSelector::PseudoClassBuffering:
+ fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesBufferingPseudoClass));
+ return FunctionType::SimpleSelectorChecker;
+ case CSSSelector::PseudoClassStalled:
+ fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesStalledPseudoClass));
+ return FunctionType::SimpleSelectorChecker;
+ case CSSSelector::PseudoClassMuted:
+ fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesMutedPseudoClass));
+ return FunctionType::SimpleSelectorChecker;
+ case CSSSelector::PseudoClassVolumeLocked:
+ fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesVolumeLockedPseudoClass));
+ return FunctionType::SimpleSelectorChecker;
#endif
#if ENABLE(ATTACHMENT_ELEMENT)
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (281502 => 281503)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2021-08-24 18:30:02 UTC (rev 281503)
@@ -392,6 +392,15 @@
return true;
}
+static bool defaultVolumeLocked()
+{
+#if PLATFORM(IOS)
+ return true;
+#else
+ return false;
+#endif
+}
+
HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document, bool createdByParser)
: HTMLElement(tagName, document)
, ActiveDOMObject(document)
@@ -439,6 +448,7 @@
, m_processingPreferenceChange(false)
, m_shouldAudioPlaybackRequireUserGesture(document.topDocument().audioPlaybackRequiresUserGesture() && !processingUserGestureForMedia())
, m_shouldVideoPlaybackRequireUserGesture(document.topDocument().videoPlaybackRequiresUserGesture() && !processingUserGestureForMedia())
+ , m_volumeLocked(defaultVolumeLocked())
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
, m_remote(RemotePlayback::create(*this))
#endif
@@ -750,6 +760,10 @@
}
else
HTMLElement::parseAttribute(name, value);
+
+ // Changing the "muted" attribue could affect ":muted"
+ if (name == mutedAttr)
+ invalidateStyle();
}
void HTMLMediaElement::finishParsingChildren()
@@ -1177,7 +1191,7 @@
m_readyStateMaximum = HAVE_NOTHING;
// 6.6 - If the paused attribute is false, then set it to true.
- m_paused = true;
+ setPaused(true);
// 6.7 - If seeking is true, set it to false.
clearSeeking();
@@ -2243,6 +2257,7 @@
if (state == MediaPlayer::NetworkState::Empty) {
// Just update the cached state and leave, we can't do anything.
m_networkState = NETWORK_EMPTY;
+ invalidateStyle();
return;
}
@@ -2271,6 +2286,8 @@
changeNetworkStateFromLoadingToIdle();
m_completelyLoaded = true;
}
+
+ invalidateStyle();
}
void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
@@ -2489,7 +2506,7 @@
// Notify about playing for the element.
auto canTransition = canTransitionFromAutoplayToPlay();
if (canTransition) {
- m_paused = false;
+ setPaused(false);
setShowPosterFlag(false);
invalidateCachedTime();
setAutoplayEventPlaybackState(AutoplayEventPlaybackState::StartedWithoutUserGesture);
@@ -2517,6 +2534,8 @@
updatePlayState();
updateMediaController();
updateActiveTextTrackCues(currentMediaTime());
+
+ invalidateStyle();
}
#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
@@ -2857,11 +2876,15 @@
if (progress) {
scheduleEvent(eventNames().progressEvent);
m_previousProgressTime = time;
- m_sentStalledEvent = false;
+ if (m_sentStalledEvent) {
+ m_sentStalledEvent = false;
+ invalidateStyle();
+ }
updateRenderer();
} else if (timedelta > 3_s && !m_sentStalledEvent) {
scheduleEvent(eventNames().stalledEvent);
m_sentStalledEvent = true;
+ invalidateStyle();
setShouldDelayLoadEvent(false);
}
});
@@ -3338,6 +3361,14 @@
return m_paused;
}
+void HTMLMediaElement::setPaused(bool paused)
+{
+ if (m_paused == paused)
+ return;
+ m_paused = paused;
+ invalidateStyle();
+}
+
double HTMLMediaElement::defaultPlaybackRate() const
{
#if ENABLE(MEDIA_STREAM)
@@ -3574,7 +3605,7 @@
m_mediaController->bringElementUpToSpeed(*this);
if (m_paused) {
- m_paused = false;
+ setPaused(false);
setShowPosterFlag(false);
invalidateCachedTime();
@@ -3665,7 +3696,7 @@
setAutoplayEventPlaybackState(AutoplayEventPlaybackState::None);
if (!m_paused && !m_pausedInternal) {
- m_paused = true;
+ setPaused(true);
scheduleTimeupdateEvent(false);
scheduleEvent(eventNames().pauseEvent);
scheduleRejectPendingPlayPromises(DOMException::create(AbortError));
@@ -3802,11 +3833,51 @@
#endif
mediaSession().canProduceAudioChanged();
updateSleepDisabling();
+
+ invalidateStyle();
}
schedulePlaybackControlsManagerUpdate();
}
+void HTMLMediaElement::setVolumeLocked(bool locked)
+{
+ if (m_volumeLocked == locked)
+ return;
+
+ m_volumeLocked = locked;
+ invalidateStyle();
+}
+
+bool HTMLMediaElement::buffering() const
+{
+ // CSS Selectors Level 4; Editor's Draft, 2 July 2021
+ // <https://drafts.csswg.org/selectors/>
+ // 11.2. Media Loading State: the :buffering and :stalled pseudo-classes
+ //
+ // The :buffering pseudo-class represents an element that is capable of being “played” or “paused”,
+ // when that element cannot continue playing because it is actively attempting to obtain media data
+ // but has not yet obtained enough data to resume playback. (Note that the element is still considered
+ // to be “playing” when it is “buffering”. Whenever :buffering matches an element, :playing also
+ // matches the element.)
+ return !paused() && m_networkState == NETWORK_LOADING && m_readyState <= HAVE_CURRENT_DATA;
+}
+
+bool HTMLMediaElement::stalled() const
+{
+ // CSS Selectors Level 4; Editor's Draft, 2 July 2021
+ // <https://drafts.csswg.org/selectors/>
+ // 11.2. Media Loading State: the :buffering and :stalled pseudo-classes
+ //
+ // The :stalled pseudo-class represents an element when that element cannot continue playing because
+ // it is actively attempting to obtain media data but it has failed to receive any data for some
+ // amount of time. For the audio and video elements of HTML, this amount of time is the media element
+ // stall timeout. [HTML] (Note that, like with the :buffering pseudo-class, the element is still
+ // considered to be “playing” when it is “stalled”. Whenever :stalled matches an element, :playing
+ // also matches the element.)
+ return !paused() && m_networkState == NETWORK_LOADING && m_readyState <= HAVE_CURRENT_DATA && m_sentStalledEvent;
+}
+
#if USE(AUDIO_SESSION) && PLATFORM(MAC)
void HTMLMediaElement::hardwareMutedStateDidChange(const AudioSession& session)
{
@@ -4849,7 +4920,7 @@
// has still ended playback and paused is false,
if (!m_mediaController && !m_paused) {
// changes paused to true and fires a simple event named pause at the media element.
- m_paused = true;
+ setPaused(true);
scheduleEvent(eventNames().pauseEvent);
mediaSession().clientWillPausePlayback();
}
@@ -4883,7 +4954,7 @@
scheduleEvent(eventNames().endedEvent);
if (!wasSeeking)
addBehaviorRestrictionsOnEndIfNecessary();
- m_paused = true;
+ setPaused(true);
setPlaying(false);
}
} else
Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (281502 => 281503)
--- trunk/Source/WebCore/html/HTMLMediaElement.h 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h 2021-08-24 18:30:02 UTC (rev 281503)
@@ -235,6 +235,7 @@
WEBCORE_EXPORT double getStartDate() const;
WEBCORE_EXPORT double duration() const override;
WEBCORE_EXPORT bool paused() const override;
+ void setPaused(bool);
double defaultPlaybackRate() const override;
void setDefaultPlaybackRate(double) override;
WEBCORE_EXPORT double playbackRate() const override;
@@ -318,6 +319,11 @@
WEBCORE_EXPORT bool muted() const override;
WEBCORE_EXPORT void setMuted(bool) override;
+ bool volumeLocked() const { return m_volumeLocked; }
+ WEBCORE_EXPORT void setVolumeLocked(bool);
+ bool buffering() const;
+ bool stalled() const;
+
WEBCORE_EXPORT void togglePlayState();
WEBCORE_EXPORT void beginScrubbing() override;
WEBCORE_EXPORT void endScrubbing() override;
@@ -1108,6 +1114,7 @@
bool m_processingPreferenceChange : 1;
bool m_shouldAudioPlaybackRequireUserGesture : 1;
bool m_shouldVideoPlaybackRequireUserGesture : 1;
+ bool m_volumeLocked : 1;
AutoplayEventPlaybackState m_autoplayEventPlaybackState { AutoplayEventPlaybackState::None };
Modified: trunk/Source/WebCore/testing/Internals.cpp (281502 => 281503)
--- trunk/Source/WebCore/testing/Internals.cpp 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/testing/Internals.cpp 2021-08-24 18:30:02 UTC (rev 281503)
@@ -4561,6 +4561,11 @@
{
return HTMLMediaElement::allMediaElements().size();
}
+
+void Internals::setMediaElementVolumeLocked(HTMLMediaElement& element, bool volumeLocked)
+{
+ element.setVolumeLocked(volumeLocked);
+}
#endif
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
Modified: trunk/Source/WebCore/testing/Internals.h (281502 => 281503)
--- trunk/Source/WebCore/testing/Internals.h 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/testing/Internals.h 2021-08-24 18:30:02 UTC (rev 281503)
@@ -981,6 +981,8 @@
MediaSessionState mediaSessionState(HTMLMediaElement&);
size_t mediaElementCount() const;
+
+ void setMediaElementVolumeLocked(HTMLMediaElement&, bool);
#endif
void setCaptureExtraNetworkLoadMetricsEnabled(bool);
Modified: trunk/Source/WebCore/testing/Internals.idl (281502 => 281503)
--- trunk/Source/WebCore/testing/Internals.idl 2021-08-24 18:18:22 UTC (rev 281502)
+++ trunk/Source/WebCore/testing/Internals.idl 2021-08-24 18:30:02 UTC (rev 281503)
@@ -957,6 +957,7 @@
[Conditional=VIDEO] MediaUsageState mediaUsageState(HTMLMediaElement element);
[Conditional=VIDEO] boolean elementShouldDisplayPosterImage(HTMLVideoElement element);
[Conditional=VIDEO] readonly attribute unsigned long mediaElementCount;
+ [Conditional=VIDEO] undefined setMediaElementVolumeLocked(HTMLMediaElement element, boolean volumeLocked);
DOMString ongoingLoadsDescriptions();
undefined setCaptureExtraNetworkLoadMetricsEnabled(boolean value);