Title: [211008] trunk/Source/WebCore
Revision
211008
Author
[email protected]
Date
2017-01-20 18:31:33 -0800 (Fri, 20 Jan 2017)

Log Message

Record whether a media element was prevented from playing without user interaction
https://bugs.webkit.org/show_bug.cgi?id=167214

Patch by Matt Rajca <[email protected]> on 2017-01-20
Reviewed by Eric Carlson.

This state will be used to notify clients when a user explicitly starts playback
of a media element that was prevented from autoplaying.

Tests will be added after a WebKit callback API is added.

* WebCore.xcodeproj/project.pbxproj:
* dom/SuccessOr.h: Added.
(WebCore::SuccessOr::SuccessOr):
(WebCore::SuccessOr::operator bool):
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::canTransitionFromAutoplayToPlay):
(WebCore::HTMLMediaElement::setReadyState):
(WebCore::HTMLMediaElement::play):
(WebCore::HTMLMediaElement::playInternal):
* html/HTMLMediaElement.h:
* html/MediaElementSession.cpp:
(WebCore::MediaElementSession::playbackPermitted):
* html/MediaElementSession.h:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (211007 => 211008)


--- trunk/Source/WebCore/ChangeLog	2017-01-21 02:24:17 UTC (rev 211007)
+++ trunk/Source/WebCore/ChangeLog	2017-01-21 02:31:33 UTC (rev 211008)
@@ -1,3 +1,29 @@
+2017-01-20  Matt Rajca  <[email protected]>
+
+        Record whether a media element was prevented from playing without user interaction
+        https://bugs.webkit.org/show_bug.cgi?id=167214
+
+        Reviewed by Eric Carlson.
+
+        This state will be used to notify clients when a user explicitly starts playback
+        of a media element that was prevented from autoplaying.
+
+        Tests will be added after a WebKit callback API is added.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/SuccessOr.h: Added.
+        (WebCore::SuccessOr::SuccessOr):
+        (WebCore::SuccessOr::operator bool):
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::canTransitionFromAutoplayToPlay):
+        (WebCore::HTMLMediaElement::setReadyState):
+        (WebCore::HTMLMediaElement::play):
+        (WebCore::HTMLMediaElement::playInternal):
+        * html/HTMLMediaElement.h:
+        * html/MediaElementSession.cpp:
+        (WebCore::MediaElementSession::playbackPermitted):
+        * html/MediaElementSession.h:
+
 2017-01-20  Brady Eidson  <[email protected]>
 
         Require a button press on a gamepad for them to be exposed to the DOM.

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (211007 => 211008)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-01-21 02:24:17 UTC (rev 211007)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-01-21 02:31:33 UTC (rev 211008)
@@ -5695,6 +5695,7 @@
 		C96F5EC61B5872260091EA9D /* MediaSessionInterruptionProviderMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = C96F5EC21B5872260091EA9D /* MediaSessionInterruptionProviderMac.mm */; };
 		C96F5EC71B5872260091EA9D /* MediaSessionInterruptionProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C96F5EC31B5872260091EA9D /* MediaSessionInterruptionProvider.cpp */; };
 		C96F5EC81B5872260091EA9D /* MediaSessionInterruptionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = C96F5EC41B5872260091EA9D /* MediaSessionInterruptionProvider.h */; };
+		C99058131E32C75F0073BDDA /* SuccessOr.h in Headers */ = {isa = PBXBuildFile; fileRef = C99058121E32B7340073BDDA /* SuccessOr.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		C9D851F01B39DC780085062E /* MediaSessionMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = C9D851EE1B39DC780085062E /* MediaSessionMetadata.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		C9DADBCB1B1D3B97001F17D8 /* JSMediaSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C9DADBC91B1D3B25001F17D8 /* JSMediaSession.cpp */; };
 		C9F87CFE1B28F40E00979B83 /* MediaSessionEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = C9F87CFD1B28E5F600979B83 /* MediaSessionEvents.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -13628,6 +13629,7 @@
 		C96F5EC21B5872260091EA9D /* MediaSessionInterruptionProviderMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MediaSessionInterruptionProviderMac.mm; sourceTree = "<group>"; };
 		C96F5EC31B5872260091EA9D /* MediaSessionInterruptionProvider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaSessionInterruptionProvider.cpp; sourceTree = "<group>"; };
 		C96F5EC41B5872260091EA9D /* MediaSessionInterruptionProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaSessionInterruptionProvider.h; sourceTree = "<group>"; };
+		C99058121E32B7340073BDDA /* SuccessOr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SuccessOr.h; sourceTree = "<group>"; };
 		C9D851EE1B39DC780085062E /* MediaSessionMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaSessionMetadata.h; sourceTree = "<group>"; };
 		C9DADBC91B1D3B25001F17D8 /* JSMediaSession.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSMediaSession.cpp; sourceTree = "<group>"; };
 		C9DADBCA1B1D3B25001F17D8 /* JSMediaSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMediaSession.h; sourceTree = "<group>"; };
@@ -24355,6 +24357,7 @@
 				81AC6C34131C57C20009A7E0 /* StringCallback.idl */,
 				A8C4A7EC09D563270003AC8D /* StyledElement.cpp */,
 				A8C4A7EB09D563270003AC8D /* StyledElement.h */,
+				C99058121E32B7340073BDDA /* SuccessOr.h */,
 				463EB6201B8789CB0096ED51 /* TagCollection.cpp */,
 				463EB6211B8789CB0096ED51 /* TagCollection.h */,
 				C65046A8167BFB5500CC2A4D /* TemplateContentDocumentFragment.h */,
@@ -27643,6 +27646,7 @@
 				84A81F420FC7E02700955300 /* SourceGraphic.h in Headers */,
 				D01A27AE10C9BFD800026A42 /* SpaceSplitString.h in Headers */,
 				626CDE0F1140424C001E5A68 /* SpatialNavigation.h in Headers */,
+				C99058131E32C75F0073BDDA /* SuccessOr.h in Headers */,
 				AA2A5AD416A4861100975A25 /* SpeechSynthesis.h in Headers */,
 				AA2A5AD216A4860A00975A25 /* SpeechSynthesisEvent.h in Headers */,
 				AA2A5AD016A4860400975A25 /* SpeechSynthesisUtterance.h in Headers */,

Added: trunk/Source/WebCore/dom/SuccessOr.h (0 => 211008)


--- trunk/Source/WebCore/dom/SuccessOr.h	                        (rev 0)
+++ trunk/Source/WebCore/dom/SuccessOr.h	2017-01-21 02:31:33 UTC (rev 211008)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+template <typename T>
+class SuccessOr : public std::optional<T> {
+public:
+    SuccessOr() : std::optional<T>() { }
+    SuccessOr(T&& error) : std::optional<T>(error) { }
+
+    explicit constexpr operator bool() const { return !std::optional<T>::operator bool(); }
+};
+
+} // namespace WebCore

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (211007 => 211008)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2017-01-21 02:24:17 UTC (rev 211007)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2017-01-21 02:31:33 UTC (rev 211008)
@@ -2253,15 +2253,17 @@
     endProcessingMediaPlayerCallback();
 }
 
-bool HTMLMediaElement::canTransitionFromAutoplayToPlay() const
+SuccessOr<MediaPlaybackDenialReason> HTMLMediaElement::canTransitionFromAutoplayToPlay() const
 {
-    return isAutoplaying()
-        && mediaSession().autoplayPermitted()
-        && paused()
-        && autoplay()
-        && !pausedForUserInteraction()
-        && !document().isSandboxed(SandboxAutomaticFeatures)
-        && mediaSession().playbackPermitted(*this);
+    if (isAutoplaying()
+     && mediaSession().autoplayPermitted()
+     && paused()
+     && autoplay()
+     && !pausedForUserInteraction()
+     && !document().isSandboxed(SandboxAutomaticFeatures))
+        return mediaSession().playbackPermitted(*this);
+
+    return MediaPlaybackDenialReason::PageConsentRequired;
 }
 
 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
@@ -2375,13 +2377,15 @@
         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
             scheduleNotifyAboutPlaying();
 
-        if (canTransitionFromAutoplayToPlay()) {
+        auto success = canTransitionFromAutoplayToPlay();
+        if (success) {
             m_paused = false;
             invalidateCachedTime();
             m_playbackStartedTime = currentMediaTime().toDouble();
             scheduleEvent(eventNames().playEvent);
             scheduleNotifyAboutPlaying();
-        }
+        } else if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
+            m_preventedFromPlayingWithoutUserGesture = true;
 
         shouldUpdateDisplayState = true;
     }
@@ -3061,7 +3065,10 @@
 {
     LOG(Media, "HTMLMediaElement::play(%p)", this);
 
-    if (!m_mediaSession->playbackPermitted(*this)) {
+    auto success = m_mediaSession->playbackPermitted(*this);
+    if (!success) {
+        if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
+            m_preventedFromPlayingWithoutUserGesture = true;
         promise.reject(NotAllowedError);
         return;
     }
@@ -3086,8 +3093,12 @@
 {
     LOG(Media, "HTMLMediaElement::play(%p)", this);
 
-    if (!m_mediaSession->playbackPermitted(*this))
+    auto success = m_mediaSession->playbackPermitted(*this);
+    if (!success) {
+        if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
+            m_preventedFromPlayingWithoutUserGesture = true;
         return;
+    }
     if (ScriptController::processingUserGestureForMedia())
         removeBehaviorsRestrictionsAfterFirstUserGesture();
 
@@ -3152,6 +3163,11 @@
     } else if (m_readyState >= HAVE_FUTURE_DATA)
         scheduleResolvePendingPlayPromises();
 
+    if (ScriptController::processingUserGestureForMedia() && m_preventedFromPlayingWithoutUserGesture) {
+        // FIXME: notify clients a user gesture was made and started playback of an element that was otherwise prevented from playing.
+        m_preventedFromPlayingWithoutUserGesture = false;
+    }
+
     m_autoplaying = false;
     updatePlayState();
 

Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (211007 => 211008)


--- trunk/Source/WebCore/html/HTMLMediaElement.h	2017-01-21 02:24:17 UTC (rev 211007)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h	2017-01-21 02:31:33 UTC (rev 211008)
@@ -719,7 +719,7 @@
     bool stoppedDueToErrors() const;
     bool pausedForUserInteraction() const;
     bool couldPlayIfEnoughData() const;
-    bool canTransitionFromAutoplayToPlay() const;
+    SuccessOr<MediaPlaybackDenialReason> canTransitionFromAutoplayToPlay() const;
 
     MediaTime minTimeSeekable() const;
     MediaTime maxTimeSeekable() const;
@@ -961,6 +961,7 @@
     bool m_creatingControls : 1;
     bool m_receivedLayoutSizeChanged : 1;
     bool m_hasEverNotifiedAboutPlaying : 1;
+    bool m_preventedFromPlayingWithoutUserGesture : 1;
 
     bool m_hasEverHadAudio : 1;
     bool m_hasEverHadVideo : 1;

Modified: trunk/Source/WebCore/html/MediaElementSession.cpp (211007 => 211008)


--- trunk/Source/WebCore/html/MediaElementSession.cpp	2017-01-21 02:24:17 UTC (rev 211007)
+++ trunk/Source/WebCore/html/MediaElementSession.cpp	2017-01-21 02:31:33 UTC (rev 211008)
@@ -148,33 +148,33 @@
     m_restrictions &= ~restriction;
 }
 
-bool MediaElementSession::playbackPermitted(const HTMLMediaElement& element) const
+SuccessOr<MediaPlaybackDenialReason> MediaElementSession::playbackPermitted(const HTMLMediaElement& element) const
 {
     if (element.document().isMediaDocument() && !element.document().ownerElement())
-        return true;
+        return SuccessOr<MediaPlaybackDenialReason>();
 
     if (pageExplicitlyAllowsElementToAutoplayInline(element))
-        return true;
+        return SuccessOr<MediaPlaybackDenialReason>();
 
     if (requiresFullscreenForVideoPlayback(element) && !fullscreenPermitted(element)) {
         LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE because of fullscreen restriction");
-        return false;
+        return MediaPlaybackDenialReason::FullscreenRequired;
     }
 
     if (m_restrictions & OverrideUserGestureRequirementForMainContent && updateIsMainContent())
-        return true;
+        return SuccessOr<MediaPlaybackDenialReason>();
 
     if (m_restrictions & RequireUserGestureForVideoRateChange && element.isVideo() && !ScriptController::processingUserGestureForMedia()) {
         LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE because of video rate change restriction");
-        return false;
+        return MediaPlaybackDenialReason::UserGestureRequired;
     }
 
     if (m_restrictions & RequireUserGestureForAudioRateChange && (!element.isVideo() || element.hasAudio()) && !element.muted() && !ScriptController::processingUserGestureForMedia()) {
         LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE because of audio rate change restriction");
-        return false;
+        return MediaPlaybackDenialReason::UserGestureRequired;
     }
 
-    return true;
+    return SuccessOr<MediaPlaybackDenialReason>();
 }
 
 bool MediaElementSession::autoplayPermitted() const

Modified: trunk/Source/WebCore/html/MediaElementSession.h (211007 => 211008)


--- trunk/Source/WebCore/html/MediaElementSession.h	2017-01-21 02:24:17 UTC (rev 211007)
+++ trunk/Source/WebCore/html/MediaElementSession.h	2017-01-21 02:31:33 UTC (rev 211008)
@@ -29,6 +29,7 @@
 
 #include "MediaPlayer.h"
 #include "PlatformMediaSession.h"
+#include "SuccessOr.h"
 #include "Timer.h"
 #include <wtf/TypeCasts.h>
 
@@ -39,6 +40,12 @@
     Autoplay
 };
 
+enum class MediaPlaybackDenialReason {
+    UserGestureRequired,
+    FullscreenRequired,
+    PageConsentRequired,
+};
+
 class Document;
 class HTMLMediaElement;
 class SourceBuffer;
@@ -52,7 +59,7 @@
     void registerWithDocument(Document&);
     void unregisterWithDocument(Document&);
 
-    bool playbackPermitted(const HTMLMediaElement&) const;
+    SuccessOr<MediaPlaybackDenialReason> playbackPermitted(const HTMLMediaElement&) const;
     bool autoplayPermitted() const;
     bool dataLoadingPermitted(const HTMLMediaElement&) const;
     bool fullscreenPermitted(const HTMLMediaElement&) const;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to