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;