summaryrefslogtreecommitdiffstats
path: root/dom/media/MediaPlaybackDelayPolicy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/MediaPlaybackDelayPolicy.cpp')
-rw-r--r--dom/media/MediaPlaybackDelayPolicy.cpp166
1 files changed, 166 insertions, 0 deletions
diff --git a/dom/media/MediaPlaybackDelayPolicy.cpp b/dom/media/MediaPlaybackDelayPolicy.cpp
new file mode 100644
index 0000000000..1ac326db7c
--- /dev/null
+++ b/dom/media/MediaPlaybackDelayPolicy.cpp
@@ -0,0 +1,166 @@
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MediaPlaybackDelayPolicy.h"
+
+#include "nsPIDOMWindow.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/StaticPrefs_media.h"
+
+namespace mozilla::dom {
+
+using AudibleState = AudioChannelService::AudibleState;
+
+static AudibleState DetermineMediaAudibleState(const HTMLMediaElement* aElement,
+ bool aIsAudible) {
+ MOZ_ASSERT(aElement);
+ if (!aElement->HasAudio()) {
+ return AudibleState::eNotAudible;
+ }
+ // `eMaybeAudible` is used to distinguish if the media has audio track or not,
+ // because we would only show the delayed media playback icon for media with
+ // an audio track.
+ return aIsAudible ? AudibleState::eAudible : AudibleState::eMaybeAudible;
+}
+
+ResumeDelayedPlaybackAgent::~ResumeDelayedPlaybackAgent() {
+ if (mDelegate) {
+ mDelegate->Clear();
+ mDelegate = nullptr;
+ }
+}
+
+bool ResumeDelayedPlaybackAgent::InitDelegate(const HTMLMediaElement* aElement,
+ bool aIsAudible) {
+ MOZ_ASSERT(!mDelegate, "Delegate has been initialized!");
+ mDelegate = new ResumePlayDelegate();
+ if (!mDelegate->Init(aElement, aIsAudible)) {
+ mDelegate->Clear();
+ mDelegate = nullptr;
+ return false;
+ }
+ return true;
+}
+
+RefPtr<ResumeDelayedPlaybackAgent::ResumePromise>
+ResumeDelayedPlaybackAgent::GetResumePromise() {
+ MOZ_ASSERT(mDelegate);
+ return mDelegate->GetResumePromise();
+}
+
+void ResumeDelayedPlaybackAgent::UpdateAudibleState(
+ const HTMLMediaElement* aElement, bool aIsAudible) {
+ MOZ_ASSERT(aElement);
+ MOZ_ASSERT(mDelegate);
+ mDelegate->UpdateAudibleState(aElement, aIsAudible);
+}
+
+NS_IMPL_ISUPPORTS(ResumeDelayedPlaybackAgent::ResumePlayDelegate,
+ nsIAudioChannelAgentCallback)
+
+ResumeDelayedPlaybackAgent::ResumePlayDelegate::~ResumePlayDelegate() {
+ MOZ_ASSERT(!mAudioChannelAgent);
+}
+
+bool ResumeDelayedPlaybackAgent::ResumePlayDelegate::Init(
+ const HTMLMediaElement* aElement, bool aIsAudible) {
+ MOZ_ASSERT(aElement);
+ if (!aElement->OwnerDoc()->GetInnerWindow()) {
+ return false;
+ }
+
+ MOZ_ASSERT(!mAudioChannelAgent);
+ mAudioChannelAgent = new AudioChannelAgent();
+ nsresult rv =
+ mAudioChannelAgent->Init(aElement->OwnerDoc()->GetInnerWindow(), this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ Clear();
+ return false;
+ }
+
+ // Start AudioChannelAgent in order to wait the suspended state change when we
+ // are able to resume delayed playback.
+ AudibleState audibleState = DetermineMediaAudibleState(aElement, aIsAudible);
+ rv = mAudioChannelAgent->NotifyStartedPlaying(audibleState);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ Clear();
+ return false;
+ }
+
+ return true;
+}
+
+void ResumeDelayedPlaybackAgent::ResumePlayDelegate::Clear() {
+ if (mAudioChannelAgent) {
+ mAudioChannelAgent->NotifyStoppedPlaying();
+ mAudioChannelAgent = nullptr;
+ }
+ mPromise.RejectIfExists(true, __func__);
+}
+
+RefPtr<ResumeDelayedPlaybackAgent::ResumePromise>
+ResumeDelayedPlaybackAgent::ResumePlayDelegate::GetResumePromise() {
+ return mPromise.Ensure(__func__);
+}
+
+void ResumeDelayedPlaybackAgent::ResumePlayDelegate::UpdateAudibleState(
+ const HTMLMediaElement* aElement, bool aIsAudible) {
+ MOZ_ASSERT(aElement);
+ // It's possible for the owner of `ResumeDelayedPlaybackAgent` to call
+ // `UpdateAudibleState()` after we resolve the promise and clean resource.
+ if (!mAudioChannelAgent) {
+ return;
+ }
+ AudibleState audibleState = DetermineMediaAudibleState(aElement, aIsAudible);
+ mAudioChannelAgent->NotifyStartedAudible(
+ audibleState,
+ AudioChannelService::AudibleChangedReasons::eDataAudibleChanged);
+}
+
+NS_IMETHODIMP
+ResumeDelayedPlaybackAgent::ResumePlayDelegate::WindowVolumeChanged(
+ float aVolume, bool aMuted) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ResumeDelayedPlaybackAgent::ResumePlayDelegate::WindowAudioCaptureChanged(
+ bool aCapture) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ResumeDelayedPlaybackAgent::ResumePlayDelegate::WindowSuspendChanged(
+ SuspendTypes aSuspend) {
+ if (aSuspend == nsISuspendedTypes::NONE_SUSPENDED) {
+ mPromise.ResolveIfExists(true, __func__);
+ Clear();
+ }
+ return NS_OK;
+}
+
+bool MediaPlaybackDelayPolicy::ShouldDelayPlayback(
+ const HTMLMediaElement* aElement) {
+ MOZ_ASSERT(aElement);
+ if (!StaticPrefs::media_block_autoplay_until_in_foreground()) {
+ return false;
+ }
+
+ const Document* doc = aElement->OwnerDoc();
+ nsPIDOMWindowInner* inner = nsPIDOMWindowInner::From(doc->GetInnerWindow());
+ nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::GetFromCurrentInner(inner);
+ return outer && outer->ShouldDelayMediaFromStart();
+}
+
+RefPtr<ResumeDelayedPlaybackAgent>
+MediaPlaybackDelayPolicy::CreateResumeDelayedPlaybackAgent(
+ const HTMLMediaElement* aElement, bool aIsAudible) {
+ MOZ_ASSERT(aElement);
+ RefPtr<ResumeDelayedPlaybackAgent> agent = new ResumeDelayedPlaybackAgent();
+ return agent->InitDelegate(aElement, aIsAudible) ? agent : nullptr;
+}
+
+} // namespace mozilla::dom