1
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "MediaPlaybackDelayPolicy.h"
7
8 #include "nsPIDOMWindow.h"
9 #include "mozilla/dom/Document.h"
10 #include "mozilla/dom/HTMLMediaElement.h"
11 #include "mozilla/StaticPrefs_media.h"
12
13 namespace mozilla::dom {
14
15 using AudibleState = AudioChannelService::AudibleState;
16
DetermineMediaAudibleState(const HTMLMediaElement * aElement,bool aIsAudible)17 static AudibleState DetermineMediaAudibleState(const HTMLMediaElement* aElement,
18 bool aIsAudible) {
19 MOZ_ASSERT(aElement);
20 if (!aElement->HasAudio()) {
21 return AudibleState::eNotAudible;
22 }
23 // `eMaybeAudible` is used to distinguish if the media has audio track or not,
24 // because we would only show the delayed media playback icon for media with
25 // an audio track.
26 return aIsAudible ? AudibleState::eAudible : AudibleState::eMaybeAudible;
27 }
28
~ResumeDelayedPlaybackAgent()29 ResumeDelayedPlaybackAgent::~ResumeDelayedPlaybackAgent() {
30 if (mDelegate) {
31 mDelegate->Clear();
32 mDelegate = nullptr;
33 }
34 }
35
InitDelegate(const HTMLMediaElement * aElement,bool aIsAudible)36 bool ResumeDelayedPlaybackAgent::InitDelegate(const HTMLMediaElement* aElement,
37 bool aIsAudible) {
38 MOZ_ASSERT(!mDelegate, "Delegate has been initialized!");
39 mDelegate = new ResumePlayDelegate();
40 if (!mDelegate->Init(aElement, aIsAudible)) {
41 mDelegate->Clear();
42 mDelegate = nullptr;
43 return false;
44 }
45 return true;
46 }
47
48 RefPtr<ResumeDelayedPlaybackAgent::ResumePromise>
GetResumePromise()49 ResumeDelayedPlaybackAgent::GetResumePromise() {
50 MOZ_ASSERT(mDelegate);
51 return mDelegate->GetResumePromise();
52 }
53
UpdateAudibleState(const HTMLMediaElement * aElement,bool aIsAudible)54 void ResumeDelayedPlaybackAgent::UpdateAudibleState(
55 const HTMLMediaElement* aElement, bool aIsAudible) {
56 MOZ_ASSERT(aElement);
57 MOZ_ASSERT(mDelegate);
58 mDelegate->UpdateAudibleState(aElement, aIsAudible);
59 }
60
NS_IMPL_ISUPPORTS(ResumeDelayedPlaybackAgent::ResumePlayDelegate,nsIAudioChannelAgentCallback)61 NS_IMPL_ISUPPORTS(ResumeDelayedPlaybackAgent::ResumePlayDelegate,
62 nsIAudioChannelAgentCallback)
63
64 ResumeDelayedPlaybackAgent::ResumePlayDelegate::~ResumePlayDelegate() {
65 MOZ_ASSERT(!mAudioChannelAgent);
66 }
67
Init(const HTMLMediaElement * aElement,bool aIsAudible)68 bool ResumeDelayedPlaybackAgent::ResumePlayDelegate::Init(
69 const HTMLMediaElement* aElement, bool aIsAudible) {
70 MOZ_ASSERT(aElement);
71 if (!aElement->OwnerDoc()->GetInnerWindow()) {
72 return false;
73 }
74
75 MOZ_ASSERT(!mAudioChannelAgent);
76 mAudioChannelAgent = new AudioChannelAgent();
77 nsresult rv =
78 mAudioChannelAgent->Init(aElement->OwnerDoc()->GetInnerWindow(), this);
79 if (NS_WARN_IF(NS_FAILED(rv))) {
80 Clear();
81 return false;
82 }
83
84 // Start AudioChannelAgent in order to wait the suspended state change when we
85 // are able to resume delayed playback.
86 AudibleState audibleState = DetermineMediaAudibleState(aElement, aIsAudible);
87 rv = mAudioChannelAgent->NotifyStartedPlaying(audibleState);
88 if (NS_WARN_IF(NS_FAILED(rv))) {
89 Clear();
90 return false;
91 }
92
93 return true;
94 }
95
Clear()96 void ResumeDelayedPlaybackAgent::ResumePlayDelegate::Clear() {
97 if (mAudioChannelAgent) {
98 mAudioChannelAgent->NotifyStoppedPlaying();
99 mAudioChannelAgent = nullptr;
100 }
101 mPromise.RejectIfExists(true, __func__);
102 }
103
104 RefPtr<ResumeDelayedPlaybackAgent::ResumePromise>
GetResumePromise()105 ResumeDelayedPlaybackAgent::ResumePlayDelegate::GetResumePromise() {
106 return mPromise.Ensure(__func__);
107 }
108
UpdateAudibleState(const HTMLMediaElement * aElement,bool aIsAudible)109 void ResumeDelayedPlaybackAgent::ResumePlayDelegate::UpdateAudibleState(
110 const HTMLMediaElement* aElement, bool aIsAudible) {
111 MOZ_ASSERT(aElement);
112 // It's possible for the owner of `ResumeDelayedPlaybackAgent` to call
113 // `UpdateAudibleState()` after we resolve the promise and clean resource.
114 if (!mAudioChannelAgent) {
115 return;
116 }
117 AudibleState audibleState = DetermineMediaAudibleState(aElement, aIsAudible);
118 mAudioChannelAgent->NotifyStartedAudible(
119 audibleState,
120 AudioChannelService::AudibleChangedReasons::eDataAudibleChanged);
121 }
122
123 NS_IMETHODIMP
WindowVolumeChanged(float aVolume,bool aMuted)124 ResumeDelayedPlaybackAgent::ResumePlayDelegate::WindowVolumeChanged(
125 float aVolume, bool aMuted) {
126 return NS_OK;
127 }
128
129 NS_IMETHODIMP
WindowAudioCaptureChanged(bool aCapture)130 ResumeDelayedPlaybackAgent::ResumePlayDelegate::WindowAudioCaptureChanged(
131 bool aCapture) {
132 return NS_OK;
133 }
134
135 NS_IMETHODIMP
WindowSuspendChanged(SuspendTypes aSuspend)136 ResumeDelayedPlaybackAgent::ResumePlayDelegate::WindowSuspendChanged(
137 SuspendTypes aSuspend) {
138 if (aSuspend == nsISuspendedTypes::NONE_SUSPENDED) {
139 mPromise.ResolveIfExists(true, __func__);
140 Clear();
141 }
142 return NS_OK;
143 }
144
ShouldDelayPlayback(const HTMLMediaElement * aElement)145 bool MediaPlaybackDelayPolicy::ShouldDelayPlayback(
146 const HTMLMediaElement* aElement) {
147 MOZ_ASSERT(aElement);
148 if (!StaticPrefs::media_block_autoplay_until_in_foreground()) {
149 return false;
150 }
151
152 const Document* doc = aElement->OwnerDoc();
153 nsPIDOMWindowInner* inner = nsPIDOMWindowInner::From(doc->GetInnerWindow());
154 nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::GetFromCurrentInner(inner);
155 return outer && outer->ShouldDelayMediaFromStart();
156 }
157
158 RefPtr<ResumeDelayedPlaybackAgent>
CreateResumeDelayedPlaybackAgent(const HTMLMediaElement * aElement,bool aIsAudible)159 MediaPlaybackDelayPolicy::CreateResumeDelayedPlaybackAgent(
160 const HTMLMediaElement* aElement, bool aIsAudible) {
161 MOZ_ASSERT(aElement);
162 RefPtr<ResumeDelayedPlaybackAgent> agent = new ResumeDelayedPlaybackAgent();
163 return agent->InitDelegate(aElement, aIsAudible) ? agent : nullptr;
164 }
165
166 } // namespace mozilla::dom
167