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