Diff
Modified: trunk/LayoutTests/ChangeLog (231816 => 231817)
--- trunk/LayoutTests/ChangeLog 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/LayoutTests/ChangeLog 2018-05-15 23:01:14 UTC (rev 231817)
@@ -1,3 +1,18 @@
+2018-05-15 Jer Noble <[email protected]>
+
+ Media continues loading after rendered invisible (removed from DOM; scrolled off screen)
+ https://bugs.webkit.org/show_bug.cgi?id=185487
+
+ Reviewed by Eric Carlson.
+
+ * media/video-buffering-allowed-expected.txt: Added.
+ * media/video-buffering-allowed.html: Added.
+ * media/video-test.js:
+ (compare):
+ (testExpected):
+ (sleepFor):
+ (testArraysEqual): Deleted.
+
2018-05-15 Charles Vazac <[email protected]>
Add the PerformanceServerTiming Interface which makes Server-Timing header timing values available to _javascript_ running in the browser.
Modified: trunk/LayoutTests/media/media-source/only-bcp47-language-tags-accepted-as-valid-expected.txt (231816 => 231817)
--- trunk/LayoutTests/media/media-source/only-bcp47-language-tags-accepted-as-valid-expected.txt 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/LayoutTests/media/media-source/only-bcp47-language-tags-accepted-as-valid-expected.txt 2018-05-15 23:01:14 UTC (rev 231817)
@@ -1,48 +1,48 @@
-CONSOLE MESSAGE: line 176: The language 'a' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'a' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'a' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'a' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language '1' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'a' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language '1' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language '1' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language '1' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'ab-abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language '1' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'ab-abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'ab-abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'ab-abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language '1a' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'ab-abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language '1a' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language '1a' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language '1a' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language '-a' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language '1a' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language '-a' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language '-a' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language '-a' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'a-' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language '-a' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'a-' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'a-' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'a-' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'a1' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'a-' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'a1' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'a1' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'a1' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'aa1' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'a1' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'aa1' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'aa1' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'aa1' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'aaaa' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'aa1' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'aaaa' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'aaaa' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'aaaa' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'aaa1' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'aaaa' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'aaa1' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'aaa1' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'aaa1' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char space' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'aaa1' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char space' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'inv-alid-char space' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char space' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char–longDash' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char space' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char–longDash' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'inv-alid-char–longDash' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char–longDash' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char-PÃ¥lska' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char–longDash' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char-PÃ¥lska' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'inv-alid-char-PÃ¥lska' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char-PÃ¥lska' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char-*' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char-PÃ¥lska' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char-*' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'inv-alid-char-*' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char-*' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char-' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char-*' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char-' is not a valid BCP 47 language tag.
CONSOLE MESSAGE: line 106: The language 'inv-alid-char-' is not a valid BCP 47 language tag.
-CONSOLE MESSAGE: line 176: The language 'inv-alid-char-' is not a valid BCP 47 language tag.
+CONSOLE MESSAGE: line 221: The language 'inv-alid-char-' is not a valid BCP 47 language tag.
Test that only BCP47 language tags are accepted as valid but still reflected.
Added: trunk/LayoutTests/media/video-buffering-allowed-expected.txt (0 => 231817)
--- trunk/LayoutTests/media/video-buffering-allowed-expected.txt (rev 0)
+++ trunk/LayoutTests/media/video-buffering-allowed-expected.txt 2018-05-15 23:01:14 UTC (rev 231817)
@@ -0,0 +1,32 @@
+
+RUN(video.src = "" "content/test"))
+EVENT(canplaythrough)
+EXPECTED (internals.elementShouldBufferData(video) == 'true') OK
+* Remove the video element from the document.
+RUN(video.parentNode.removeChild(video))
+EXPECTED (internals.elementShouldBufferData(video) == 'false') OK
+* Play the video.
+RUN(video.play())
+EVENT(playing)
+EXPECTED (internals.elementShouldBufferData(video) == 'true') OK
+* Pause the video.
+RUN(video.pause())
+EVENT(pause)
+EXPECTED (internals.elementShouldBufferData(video) == 'true') OK
+* Re-insert the video element into the document.
+RUN(document.body.insertBefore(video, document.body.firstChild))
+EXPECTED (internals.elementShouldBufferData(video) == 'true') OK
+* display:none the video element.
+RUN(video.style.display = "none")
+EXPECTED (internals.elementShouldBufferData(video) == 'true') OK
+* Simulate the view becoming invisible.
+RUN(internals.setPageVisibility(false))
+EXPECTED (internals.elementShouldBufferData(video) == 'false') OK
+* Simulate the view becoming visible.
+RUN(internals.setPageVisibility(true))
+EXPECTED (internals.elementShouldBufferData(video) == 'false') OK
+* Remove display:none from the video element.
+RUN(video.style.removeProperty("display"))
+EXPECTED (internals.elementShouldBufferData(video) == 'true') OK
+END OF TEST
+
Added: trunk/LayoutTests/media/video-buffering-allowed.html (0 => 231817)
--- trunk/LayoutTests/media/video-buffering-allowed.html (rev 0)
+++ trunk/LayoutTests/media/video-buffering-allowed.html 2018-05-15 23:01:14 UTC (rev 231817)
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>video-buffering-allowed</title>
+ <script src=""
+ <script src=""
+ <script>
+ async function runTest() {
+ findMediaElement();
+ failTestIn(1000);
+
+ run('video.src = "" "content/test")');
+ await waitFor(video, 'canplaythrough');
+ testExpected('internals.elementShouldBufferData(video)', true);
+
+ consoleWrite('* Remove the video element from the document.');
+ run('video.parentNode.removeChild(video)');
+ await testExpectedEventually('internals.elementShouldBufferData(video)', false);
+
+ consoleWrite('* Play the video.');
+ run ('video.play()');
+ await waitFor(video, 'playing');
+ testExpected('internals.elementShouldBufferData(video)', true);
+
+ consoleWrite('* Pause the video.');
+ run('video.pause()');
+ await waitFor(video, 'pause');
+ testExpected('internals.elementShouldBufferData(video)', true);
+
+ consoleWrite('* Re-insert the video element into the document.');
+ run('document.body.insertBefore(video, document.body.firstChild)');
+ await testExpectedEventually('internals.elementShouldBufferData(video)', true);
+
+ consoleWrite('* display:none the video element.');
+ run('video.style.display = "none"');
+ await testExpectedEventually('internals.elementShouldBufferData(video)', true);
+
+ consoleWrite('* Simulate the view becoming invisible.');
+ run('internals.setPageVisibility(false)');
+ await testExpectedEventually('internals.elementShouldBufferData(video)', false);
+
+ consoleWrite('* Simulate the view becoming visible.');
+ run('internals.setPageVisibility(true)');
+ await testExpectedEventually('internals.elementShouldBufferData(video)', false);
+
+ consoleWrite('* Remove display:none from the video element.');
+ run('video.style.removeProperty("display")');
+ await testExpectedEventually('internals.elementShouldBufferData(video)', true);
+
+ endTest();
+ }
+ window.addEventListener('load', runTest);
+ </script>
+</head>
+<body>
+ <video controls></video>
+</body>
+</html>
Modified: trunk/LayoutTests/media/video-test.js (231816 => 231817)
--- trunk/LayoutTests/media/video-test.js 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/LayoutTests/media/video-test.js 2018-05-15 23:01:14 UTC (rev 231817)
@@ -62,18 +62,10 @@
endTest();
}
-function testExpected(testFuncString, expected, comparison)
+function compare(testFuncString, expected, comparison)
{
- try {
- var observed = eval(testFuncString);
- } catch (ex) {
- consoleWrite(ex);
- return;
- }
+ var observed = eval(testFuncString);
- if (comparison === undefined)
- comparison = '==';
-
var success = false;
switch (comparison)
{
@@ -87,9 +79,53 @@
case 'instanceof': success = observed instanceof expected; break;
}
- reportExpected(success, testFuncString, comparison, expected, observed)
+ return {success:success, observed:observed};
}
+function testExpected(testFuncString, expected, comparison)
+{
+ if (comparison === undefined)
+ comparison = '==';
+
+ try {
+ let {success, observed} = compare(testFuncString, expected, comparison);
+ reportExpected(success, testFuncString, comparison, expected, observed)
+ } catch (ex) {
+ consoleWrite(ex);
+ }
+}
+
+function sleepFor(duration) {
+ return new Promise(resolve => {
+ setTimeout(resolve, duration);
+ });
+}
+
+function testExpectedEventually(testFuncString, expected, comparison)
+{
+ return new Promise(async resolve => {
+ var success;
+ var observed;
+ if (comparison === undefined)
+ comparison = '==';
+ while (true) {
+ try {
+ let {success, observed} = compare(testFuncString, expected, comparison);
+ if (success) {
+ reportExpected(success, testFuncString, comparison, expected, observed);
+ resolve();
+ return;
+ }
+ await sleepFor(1);
+ } catch (ex) {
+ consoleWrite(ex);
+ resolve();
+ return;
+ }
+ }
+ });
+}
+
function testArraysEqual(testFuncString, expected)
{
var observed;
@@ -150,6 +186,15 @@
}
}
+function waitFor(element, event) {
+ return new Promise(resolve => {
+ element.addEventListener(event, event => {
+ consoleWrite(`EVENT(${event.type})`);
+ resolve(event);
+ }, { once: true });
+ });
+}
+
function waitForEventOnce(eventName, func, endit)
{
waitForEvent(eventName, func, endit, true)
Modified: trunk/Source/WebCore/ChangeLog (231816 => 231817)
--- trunk/Source/WebCore/ChangeLog 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/ChangeLog 2018-05-15 23:01:14 UTC (rev 231817)
@@ -1,3 +1,82 @@
+2018-05-15 Jer Noble <[email protected]>
+
+ Media continues loading after rendered invisible (removed from DOM; scrolled off screen)
+ https://bugs.webkit.org/show_bug.cgi?id=185487
+
+ Reviewed by Eric Carlson.
+
+ Test: media/video-buffering-allowed.html
+
+ When a media element is removed from the dom (e.g. through innerHTML=""), it doesn't
+ necessarily stop loading media data; it will continue to do so until its destructor is
+ called through garbage collection. Similarly, when a media element is rendered not-visible
+ by being scrolled off-screen or being made display:none, media loading continues. There
+ are legitimate use cases for out-of-DOM media loading, so only temporarily block loading
+ when the element transitions out of the document. Similarly, only block loading for non-visible
+ media elements when returning from the "page is hidden" state, and only until the media
+ element is asked to play or is otherwise made visible.
+
+ Note: this refactors a lot of code out of PlatformMediaSession and into MediaElementSession,
+ since this code is specific to "media elements".
+
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::HTMLMediaElement):
+ (WebCore::HTMLMediaElement::insertedIntoAncestor):
+ (WebCore::HTMLMediaElement::removedFromAncestor):
+ (WebCore::HTMLMediaElement::playInternal):
+ (WebCore::HTMLMediaElement::stopWithoutDestroyingMediaPlayer):
+ (WebCore::HTMLMediaElement::resume):
+ (WebCore::HTMLMediaElement::visibilityStateChanged):
+ (WebCore::HTMLMediaElement::createMediaPlayer):
+ (WebCore::HTMLMediaElement::setShouldBufferData):
+ (WebCore::HTMLMediaElement::purgeBufferedDataIfPossible):
+ (WebCore::HTMLMediaElement::isVisibleInViewportChanged):
+ (WebCore::HTMLMediaElement::fullscreenModeChanged):
+ (WebCore::HTMLMediaElement::setInActiveDocument):
+ * html/HTMLMediaElement.h:
+ (WebCore::HTMLMediaElement::shouldBufferData const):
+ (WebCore::HTMLMediaElement::elementIsHidden const):
+ * html/MediaElementSession.cpp:
+ (WebCore::MediaElementSession::MediaElementSession):
+ (WebCore::MediaElementSession::clientWillBeginAutoplaying):
+ (WebCore::MediaElementSession::clientWillBeginPlayback):
+ (WebCore::MediaElementSession::clientWillPausePlayback):
+ (WebCore::MediaElementSession::visibilityChanged):
+ (WebCore::MediaElementSession::isVisibleInViewportChanged):
+ (WebCore::MediaElementSession::inActiveDocumentChanged):
+ (WebCore::MediaElementSession::scheduleClientDataBufferingCheck):
+ (WebCore::MediaElementSession::clientDataBufferingTimerFired):
+ (WebCore::MediaElementSession::updateClientDataBuffering):
+ (WebCore::MediaElementSession::dataBufferingPermitted const):
+ (WebCore::MediaElementSession::wantsToObserveViewportVisibilityForAutoplay const):
+ * html/MediaElementSession.h:
+ * platform/audio/PlatformMediaSession.cpp:
+ (WebCore::PlatformMediaSession::PlatformMediaSession):
+ (WebCore::PlatformMediaSession::clientWillBeginAutoplaying):
+ (WebCore::PlatformMediaSession::clientWillBeginPlayback):
+ (WebCore::PlatformMediaSession::clientWillPausePlayback):
+ (): Deleted.
+ (WebCore::PlatformMediaSession::visibilityChanged): Deleted.
+ (WebCore::PlatformMediaSession::scheduleClientDataBufferingCheck): Deleted.
+ (WebCore::PlatformMediaSession::clientDataBufferingTimerFired): Deleted.
+ (WebCore::PlatformMediaSession::updateClientDataBuffering): Deleted.
+ (WebCore::PlatformMediaSession::isHidden const): Deleted.
+ * platform/audio/PlatformMediaSession.h:
+ (WebCore::PlatformMediaSessionClient::setShouldBufferData): Deleted.
+ (WebCore::PlatformMediaSessionClient::elementIsHidden const): Deleted.
+ * platform/audio/PlatformMediaSessionManager.cpp:
+ (WebCore::PlatformMediaSessionManager::sessionCanLoadMedia const): Deleted.
+ * platform/audio/PlatformMediaSessionManager.h:
+ * platform/audio/ios/MediaSessionManagerIOS.h:
+ * platform/audio/ios/MediaSessionManagerIOS.mm:
+ (WebCore::MediaSessionManageriOS::sessionCanLoadMedia const): Deleted.
+ * rendering/RenderVideo.cpp:
+ (WebCore::RenderVideo::willBeDestroyed):
+ * testing/Internals.cpp:
+ (WebCore::Internals::elementShouldBufferData):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
2018-05-15 Charles Vazac <[email protected]>
Add the PerformanceServerTiming Interface which makes Server-Timing header timing values available to _javascript_ running in the browser.
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (231816 => 231817)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2018-05-15 23:01:14 UTC (rev 231817)
@@ -477,6 +477,7 @@
, m_completelyLoaded(false)
, m_havePreparedToPlay(false)
, m_parsingInProgress(createdByParser)
+ , m_shouldBufferData(true)
, m_elementIsHidden(document.hidden())
, m_creatingControls(false)
, m_receivedLayoutSizeChanged(false)
@@ -926,7 +927,7 @@
HTMLElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
if (insertionType.connectedToDocument)
- m_inActiveDocument = true;
+ setInActiveDocument(true);
return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
}
@@ -978,7 +979,7 @@
{
INFO_LOG(LOGIDENTIFIER);
- m_inActiveDocument = false;
+ setInActiveDocument(false);
if (removalType.disconnectedFromDocument) {
// Pause asynchronously to let the operation that removed us finish, in case we get inserted back into a document.
m_pauseAfterDetachedTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::pauseAfterDetachedTask, this));
@@ -3467,7 +3468,7 @@
ALWAYS_LOG(LOGIDENTIFIER, " returning because of interruption");
return;
}
-
+
// 4.8.10.9. Playing the media resource
if (!m_player || m_networkState == NETWORK_EMPTY)
selectMediaResource();
@@ -5520,7 +5521,7 @@
setPreparedToReturnVideoLayerToInline(true);
updatePlaybackControlsManager();
- m_inActiveDocument = false;
+ setInActiveDocument(false);
// Stop the playback without generating events
setPlaying(false);
@@ -5600,7 +5601,7 @@
{
INFO_LOG(LOGIDENTIFIER);
- m_inActiveDocument = true;
+ setInActiveDocument(true);
m_asyncEventQueue.resume();
@@ -5638,8 +5639,13 @@
void HTMLMediaElement::visibilityStateChanged()
{
- m_elementIsHidden = document().hidden() && m_videoFullscreenMode != VideoFullscreenModePictureInPicture;
+ bool elementIsHidden = document().hidden() && m_videoFullscreenMode != VideoFullscreenModePictureInPicture;
+ if (elementIsHidden == m_elementIsHidden)
+ return;
+
+ m_elementIsHidden = elementIsHidden;
INFO_LOG(LOGIDENTIFIER, "visible = ", !m_elementIsHidden);
+
updateSleepDisabling();
m_mediaSession->visibilityChanged();
if (m_player)
@@ -6499,6 +6505,7 @@
forgetResourceSpecificTracks();
#endif
m_player = MediaPlayer::create(*this);
+ m_player->setShouldBufferData(m_shouldBufferData);
scheduleUpdatePlaybackControlsManager();
#if ENABLE(WEB_AUDIO)
@@ -7660,6 +7667,10 @@
void HTMLMediaElement::setShouldBufferData(bool shouldBuffer)
{
+ if (shouldBuffer == m_shouldBufferData)
+ return;
+
+ m_shouldBufferData = shouldBuffer;
if (m_player)
m_player->setShouldBufferData(shouldBuffer);
}
@@ -7667,7 +7678,7 @@
void HTMLMediaElement::purgeBufferedDataIfPossible()
{
#if PLATFORM(IOS)
- if (!MemoryPressureHandler::singleton().isUnderMemoryPressure() && PlatformMediaSessionManager::sharedManager().sessionCanLoadMedia(*m_mediaSession))
+ if (!MemoryPressureHandler::singleton().isUnderMemoryPressure() && m_mediaSession->dataBufferingPermitted())
return;
if (m_isPlayingToWirelessTarget) {
@@ -7775,6 +7786,7 @@
void HTMLMediaElement::isVisibleInViewportChanged()
{
m_visibilityChangeTaskQueue.enqueueTask([this] {
+ m_mediaSession->isVisibleInViewportChanged();
updateShouldAutoplay();
scheduleUpdatePlaybackControlsManager();
});
@@ -7874,7 +7886,6 @@
m_videoFullscreenMode = mode;
visibilityStateChanged();
- m_mediaSession->scheduleClientDataBufferingCheck();
scheduleUpdatePlaybackControlsManager();
}
@@ -7907,6 +7918,15 @@
m_player->applicationDidBecomeActive();
}
+void HTMLMediaElement::setInActiveDocument(bool inActiveDocument)
+{
+ if (inActiveDocument == m_inActiveDocument)
+ return;
+
+ m_inActiveDocument = inActiveDocument;
+ m_mediaSession->inActiveDocumentChanged();
}
+}
+
#endif
Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (231816 => 231817)
--- trunk/Source/WebCore/html/HTMLMediaElement.h 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h 2018-05-15 23:01:14 UTC (rev 231817)
@@ -265,7 +265,8 @@
WEBCORE_EXPORT void play() override;
WEBCORE_EXPORT void pause() override;
- void setShouldBufferData(bool) override;
+ void setShouldBufferData(bool);
+ WEBCORE_EXPORT bool shouldBufferData() const { return m_shouldBufferData; }
WEBCORE_EXPORT void fastSeek(double);
double minFastReverseRate() const;
double maxFastForwardRate() const;
@@ -277,7 +278,7 @@
WEBCORE_EXPORT bool webkitClosedCaptionsVisible() const;
WEBCORE_EXPORT void setWebkitClosedCaptionsVisible(bool);
- bool elementIsHidden() const override { return m_elementIsHidden; }
+ bool elementIsHidden() const { return m_elementIsHidden; }
#if ENABLE(MEDIA_STATISTICS)
// Statistics
@@ -912,6 +913,8 @@
void applicationWillResignActive() final;
void applicationDidBecomeActive() final;
+ void setInActiveDocument(bool);
+
#if !RELEASE_LOG_DISABLED
const char* logClassName() const final { return "HTMLMediaElement"; }
@@ -1064,7 +1067,9 @@
bool m_completelyLoaded : 1;
bool m_havePreparedToPlay : 1;
bool m_parsingInProgress : 1;
+ bool m_shouldBufferData : 1;
bool m_elementIsHidden : 1;
+ bool m_elementWasRemovedFromDOM : 1;
bool m_creatingControls : 1;
bool m_receivedLayoutSizeChanged : 1;
bool m_hasEverNotifiedAboutPlaying : 1;
Modified: trunk/Source/WebCore/html/MediaElementSession.cpp (231816 => 231817)
--- trunk/Source/WebCore/html/MediaElementSession.cpp 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/html/MediaElementSession.cpp 2018-05-15 23:01:14 UTC (rev 231817)
@@ -56,6 +56,7 @@
namespace WebCore {
+static const Seconds clientDataBufferingTimerThrottleDelay { 100_ms };
static const Seconds elementMainContentCheckInterval { 250_ms };
static bool isElementRectMostlyInMainFrame(const HTMLMediaElement&);
@@ -109,6 +110,7 @@
, m_targetAvailabilityChangedTimer(*this, &MediaElementSession::targetAvailabilityChangedTimerFired)
#endif
, m_mainContentCheckTimer(*this, &MediaElementSession::mainContentCheckTimerFired)
+ , m_clientDataBufferingTimer(*this, &MediaElementSession::clientDataBufferingTimerFired)
#if !RELEASE_LOG_DISABLED
, m_logIdentifier(element.logIdentifier())
#endif
@@ -133,6 +135,86 @@
#endif
}
+void MediaElementSession::clientWillBeginAutoplaying()
+{
+ PlatformMediaSession::clientWillBeginAutoplaying();
+ m_elementIsHiddenBecauseItWasRemovedFromDOM = false;
+ updateClientDataBuffering();
+}
+
+bool MediaElementSession::clientWillBeginPlayback()
+{
+ if (!PlatformMediaSession::clientWillBeginPlayback())
+ return false;
+
+ m_elementIsHiddenBecauseItWasRemovedFromDOM = false;
+ updateClientDataBuffering();
+ return true;
+}
+
+bool MediaElementSession::clientWillPausePlayback()
+{
+ if (!PlatformMediaSession::clientWillPausePlayback())
+ return false;
+
+ updateClientDataBuffering();
+ return true;
+}
+
+void MediaElementSession::visibilityChanged()
+{
+ scheduleClientDataBufferingCheck();
+
+ if (m_element.elementIsHidden() && !m_element.isFullscreen())
+ m_elementIsHiddenUntilVisibleInViewport = true;
+}
+
+void MediaElementSession::isVisibleInViewportChanged()
+{
+ scheduleClientDataBufferingCheck();
+
+ if (m_element.isFullscreen() || m_element.isVisibleInViewport())
+ m_elementIsHiddenUntilVisibleInViewport = false;
+}
+
+void MediaElementSession::inActiveDocumentChanged()
+{
+ m_elementIsHiddenBecauseItWasRemovedFromDOM = !m_element.inActiveDocument();
+ scheduleClientDataBufferingCheck();
+}
+
+void MediaElementSession::scheduleClientDataBufferingCheck()
+{
+ if (!m_clientDataBufferingTimer.isActive())
+ m_clientDataBufferingTimer.startOneShot(clientDataBufferingTimerThrottleDelay);
+}
+
+void MediaElementSession::clientDataBufferingTimerFired()
+{
+ INFO_LOG(LOGIDENTIFIER, "visible = ", m_element.elementIsHidden());
+
+ updateClientDataBuffering();
+
+#if PLATFORM(IOS)
+ PlatformMediaSessionManager::sharedManager().configureWireLessTargetMonitoring();
+#endif
+
+ if (state() != Playing || !m_element.elementIsHidden())
+ return;
+
+ PlatformMediaSessionManager::SessionRestrictions restrictions = PlatformMediaSessionManager::sharedManager().restrictions(mediaType());
+ if ((restrictions & PlatformMediaSessionManager::BackgroundTabPlaybackRestricted) == PlatformMediaSessionManager::BackgroundTabPlaybackRestricted)
+ pauseSession();
+}
+
+void MediaElementSession::updateClientDataBuffering()
+{
+ if (m_clientDataBufferingTimer.isActive())
+ m_clientDataBufferingTimer.stop();
+
+ m_element.setShouldBufferData(dataBufferingPermitted());
+}
+
void MediaElementSession::addBehaviorRestriction(BehaviorRestrictions restrictions)
{
if (restrictions & ~m_restrictions)
@@ -275,6 +357,23 @@
return true;
}
+bool MediaElementSession::dataBufferingPermitted() const
+{
+ if (isSuspended())
+ return false;
+
+ if (state() == PlatformMediaSession::Playing)
+ return true;
+
+ if (shouldOverrideBackgroundLoadingRestriction())
+ return true;
+
+ if (m_elementIsHiddenUntilVisibleInViewport || m_elementIsHiddenBecauseItWasRemovedFromDOM || m_element.elementIsHidden())
+ return false;
+
+ return true;
+}
+
bool MediaElementSession::fullscreenPermitted() const
{
if (m_restrictions & RequireUserGestureForFullscreen && !m_element.document().processingUserGestureForMedia()) {
@@ -418,9 +517,7 @@
bool MediaElementSession::wantsToObserveViewportVisibilityForAutoplay() const
{
- if (!m_element.isVideo())
- return false;
- return hasBehaviorRestriction(InvisibleAutoplayNotPermitted) || hasBehaviorRestriction(OverrideUserGestureRequirementForMainContent);
+ return m_element.isVideo();
}
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
@@ -674,7 +771,7 @@
#if PLATFORM(IOS)
bool MediaElementSession::requiresPlaybackTargetRouteMonitoring() const
{
- return m_hasPlaybackTargetAvailabilityListeners && !client().elementIsHidden();
+ return m_hasPlaybackTargetAvailabilityListeners && !m_element.elementIsHidden();
}
#endif
Modified: trunk/Source/WebCore/html/MediaElementSession.h (231816 => 231817)
--- trunk/Source/WebCore/html/MediaElementSession.h 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/html/MediaElementSession.h 2018-05-15 23:01:14 UTC (rev 231817)
@@ -61,9 +61,18 @@
void registerWithDocument(Document&);
void unregisterWithDocument(Document&);
+ void clientWillBeginAutoplaying() final;
+ bool clientWillBeginPlayback() final;
+ bool clientWillPausePlayback() final;
+
+ void visibilityChanged();
+ void isVisibleInViewportChanged();
+ void inActiveDocumentChanged();
+
SuccessOr<MediaPlaybackDenialReason> playbackPermitted() const;
bool autoplayPermitted() const;
bool dataLoadingPermitted() const;
+ bool dataBufferingPermitted() const;
bool fullscreenPermitted() const;
bool pageAllowsDataLoading() const;
bool pageAllowsPlaybackAfterResuming() const;
@@ -165,9 +174,16 @@
bool updateIsMainContent() const;
void mainContentCheckTimerFired();
+ void scheduleClientDataBufferingCheck();
+ void clientDataBufferingTimerFired();
+ void updateClientDataBuffering();
+
HTMLMediaElement& m_element;
BehaviorRestrictions m_restrictions;
+ bool m_elementIsHiddenUntilVisibleInViewport { false };
+ bool m_elementIsHiddenBecauseItWasRemovedFromDOM { false };
+
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
mutable Timer m_targetAvailabilityChangedTimer;
RefPtr<MediaPlaybackTarget> m_playbackTarget;
@@ -182,6 +198,7 @@
mutable bool m_isMainContent { false };
Timer m_mainContentCheckTimer;
+ Timer m_clientDataBufferingTimer;
#if !RELEASE_LOG_DISABLED
const void* m_logIdentifier;
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp (231816 => 231817)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSession.cpp 2018-05-15 23:01:14 UTC (rev 231817)
@@ -93,7 +93,6 @@
PlatformMediaSession::PlatformMediaSession(PlatformMediaSessionClient& client)
: m_client(client)
- , m_clientDataBufferingTimer(*this, &PlatformMediaSession::clientDataBufferingTimerFired)
, m_state(Idle)
, m_stateToRestore(Idle)
, m_notifyingClient(false)
@@ -176,7 +175,6 @@
}
setState(Autoplaying);
- updateClientDataBuffering();
}
bool PlatformMediaSession::clientWillBeginPlayback()
@@ -191,7 +189,6 @@
}
setState(Playing);
- updateClientDataBuffering();
return true;
}
@@ -209,7 +206,6 @@
setState(Paused);
PlatformMediaSessionManager::sharedManager().sessionWillEndPlayback(*this);
- scheduleClientDataBufferingCheck();
return true;
}
@@ -273,53 +269,11 @@
return m_client.supportsSeeking();
}
-void PlatformMediaSession::visibilityChanged()
-{
- scheduleClientDataBufferingCheck();
-}
-
-void PlatformMediaSession::scheduleClientDataBufferingCheck()
-{
- if (!m_clientDataBufferingTimer.isActive())
- m_clientDataBufferingTimer.startOneShot(clientDataBufferingTimerThrottleDelay);
-}
-
-void PlatformMediaSession::clientDataBufferingTimerFired()
-{
- INFO_LOG(LOGIDENTIFIER, "visible = ", m_client.elementIsHidden());
-
- updateClientDataBuffering();
-
-#if PLATFORM(IOS)
- PlatformMediaSessionManager::sharedManager().configureWireLessTargetMonitoring();
-#endif
-
- if (m_state != Playing || !m_client.elementIsHidden())
- return;
-
- PlatformMediaSessionManager::SessionRestrictions restrictions = PlatformMediaSessionManager::sharedManager().restrictions(mediaType());
- if ((restrictions & PlatformMediaSessionManager::BackgroundTabPlaybackRestricted) == PlatformMediaSessionManager::BackgroundTabPlaybackRestricted)
- pauseSession();
-}
-
-void PlatformMediaSession::updateClientDataBuffering()
-{
- if (m_clientDataBufferingTimer.isActive())
- m_clientDataBufferingTimer.stop();
-
- m_client.setShouldBufferData(PlatformMediaSessionManager::sharedManager().sessionCanLoadMedia(*this));
-}
-
String PlatformMediaSession::sourceApplicationIdentifier() const
{
return m_client.sourceApplicationIdentifier();
}
-bool PlatformMediaSession::isHidden() const
-{
- return m_client.elementIsHidden();
-}
-
bool PlatformMediaSession::isSuspended() const
{
return m_client.isSuspended();
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSession.h (231816 => 231817)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSession.h 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSession.h 2018-05-15 23:01:14 UTC (rev 231817)
@@ -105,15 +105,13 @@
void beginInterruption(InterruptionType);
void endInterruption(EndInterruptionFlags);
- void clientWillBeginAutoplaying();
- bool clientWillBeginPlayback();
- bool clientWillPausePlayback();
+ virtual void clientWillBeginAutoplaying();
+ virtual bool clientWillBeginPlayback();
+ virtual bool clientWillPausePlayback();
void pauseSession();
void stopSession();
- void visibilityChanged();
-
#if ENABLE(VIDEO)
String title() const;
double duration() const;
@@ -171,7 +169,6 @@
bool canProduceAudio() const;
void canProduceAudioChanged();
- void scheduleClientDataBufferingCheck();
virtual void resetPlaybackSessionState() { }
String sourceApplicationIdentifier() const;
@@ -188,11 +185,7 @@
#endif
private:
- void clientDataBufferingTimerFired();
- void updateClientDataBuffering();
-
PlatformMediaSessionClient& m_client;
- Timer m_clientDataBufferingTimer;
State m_state;
State m_stateToRestore;
InterruptionType m_interruptionType { NoInterruption };
@@ -232,8 +225,6 @@
virtual void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType, const PlatformMediaSession::RemoteCommandArgument*) = 0;
virtual bool supportsSeeking() const = 0;
- virtual void setShouldBufferData(bool) { }
- virtual bool elementIsHidden() const { return false; }
virtual bool canProduceAudio() const { return false; }
virtual bool isSuspended() const { return false; };
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp (231816 => 231817)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.cpp 2018-05-15 23:01:14 UTC (rev 231817)
@@ -287,13 +287,6 @@
return matchingSessions;
}
-bool PlatformMediaSessionManager::sessionCanLoadMedia(const PlatformMediaSession& session) const
-{
- if (session.isSuspended())
- return false;
- return session.state() == PlatformMediaSession::Playing || !session.isHidden() || session.shouldOverrideBackgroundLoadingRestriction();
-}
-
void PlatformMediaSessionManager::applicationWillBecomeInactive() const
{
LOG(Media, "PlatformMediaSessionManager::applicationWillBecomeInactive");
Modified: trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h (231816 => 231817)
--- trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/platform/audio/PlatformMediaSessionManager.h 2018-05-15 23:01:14 UTC (rev 231817)
@@ -94,7 +94,6 @@
virtual bool sessionWillBeginPlayback(PlatformMediaSession&);
virtual void sessionWillEndPlayback(PlatformMediaSession&);
- virtual bool sessionCanLoadMedia(const PlatformMediaSession&) const;
virtual void sessionDidEndRemoteScrubbing(const PlatformMediaSession&) { };
virtual void clientCharacteristicsChanged(PlatformMediaSession&) { }
Modified: trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.h (231816 => 231817)
--- trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.h 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.h 2018-05-15 23:01:14 UTC (rev 231817)
@@ -65,8 +65,6 @@
void configureWireLessTargetMonitoring() override;
- bool sessionCanLoadMedia(const PlatformMediaSession&) const override;
-
bool hasActiveNowPlayingSession() const final { return m_nowPlayingActive; }
String lastUpdatedNowPlayingTitle() const final { return m_reportedTitle; }
double lastUpdatedNowPlayingDuration() const final { return m_reportedDuration; }
Modified: trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm (231816 => 231817)
--- trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/platform/audio/ios/MediaSessionManagerIOS.mm 2018-05-15 23:01:14 UTC (rev 231817)
@@ -301,14 +301,6 @@
#endif // HAVE(MEDIA_PLAYER)
}
-bool MediaSessionManageriOS::sessionCanLoadMedia(const PlatformMediaSession& session) const
-{
- if (session.displayType() == PlatformMediaSession::Optimized)
- return true;
-
- return PlatformMediaSessionManager::sessionCanLoadMedia(session);
-}
-
void MediaSessionManageriOS::externalOutputDeviceAvailableDidChange()
{
BEGIN_BLOCK_OBJC_EXCEPTIONS
Modified: trunk/Source/WebCore/rendering/RenderVideo.cpp (231816 => 231817)
--- trunk/Source/WebCore/rendering/RenderVideo.cpp 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/rendering/RenderVideo.cpp 2018-05-15 23:01:14 UTC (rev 231817)
@@ -64,6 +64,7 @@
void RenderVideo::willBeDestroyed()
{
+ visibleInViewportStateChanged();
if (auto player = videoElement().player())
player->setVisible(false);
Modified: trunk/Source/WebCore/testing/Internals.cpp (231816 => 231817)
--- trunk/Source/WebCore/testing/Internals.cpp 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/testing/Internals.cpp 2018-05-15 23:01:14 UTC (rev 231817)
@@ -3232,6 +3232,11 @@
player->endSimulatedHDCPError();
}
+bool Internals::elementShouldBufferData(HTMLMediaElement& element)
+{
+ return element.shouldBufferData();
+}
+
#endif
bool Internals::isSelectPopupVisible(HTMLSelectElement& element)
Modified: trunk/Source/WebCore/testing/Internals.h (231816 => 231817)
--- trunk/Source/WebCore/testing/Internals.h 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/testing/Internals.h 2018-05-15 23:01:14 UTC (rev 231817)
@@ -473,6 +473,8 @@
ExceptionOr<bool> mediaElementHasCharacteristic(HTMLMediaElement&, const String&);
void beginSimulatedHDCPError(HTMLMediaElement&);
void endSimulatedHDCPError(HTMLMediaElement&);
+
+ bool elementShouldBufferData(HTMLMediaElement&);
#endif
bool isSelectPopupVisible(HTMLSelectElement&);
Modified: trunk/Source/WebCore/testing/Internals.idl (231816 => 231817)
--- trunk/Source/WebCore/testing/Internals.idl 2018-05-15 21:52:44 UTC (rev 231816)
+++ trunk/Source/WebCore/testing/Internals.idl 2018-05-15 23:01:14 UTC (rev 231817)
@@ -429,6 +429,8 @@
[Conditional=VIDEO] void beginSimulatedHDCPError(HTMLMediaElement media);
[Conditional=VIDEO] void endSimulatedHDCPError(HTMLMediaElement media);
+ [Conditional=VIDEO] boolean elementShouldBufferData(HTMLMediaElement media);
+
[Conditional=LEGACY_ENCRYPTED_MEDIA] void initializeMockCDM();
[Conditional=ENCRYPTED_MEDIA] MockCDMFactory registerMockCDM();