AudioChannelAgent.cpp (9251B)
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 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "AudioChannelAgent.h" 6 7 #include "AudioChannelService.h" 8 #include "mozilla/Preferences.h" 9 #include "mozilla/dom/Document.h" 10 #include "nsContentUtils.h" 11 #include "nsPIDOMWindow.h" 12 13 using namespace mozilla::dom; 14 15 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioChannelAgent) 16 17 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioChannelAgent) 18 tmp->Shutdown(); 19 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) 20 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) 21 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 22 23 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioChannelAgent) 24 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) 25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) 26 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 27 28 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgent) 29 NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgent) 30 NS_INTERFACE_MAP_ENTRY(nsISupports) 31 NS_INTERFACE_MAP_END 32 33 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent) 34 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent) 35 36 AudioChannelAgent::AudioChannelAgent() 37 : mInnerWindowID(0), mIsRegToService(false) { 38 // Init service in the begining, it can help us to know whether there is any 39 // created media component via AudioChannelService::IsServiceStarted(). 40 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); 41 } 42 43 AudioChannelAgent::~AudioChannelAgent() { Shutdown(); } 44 45 void AudioChannelAgent::Shutdown() { 46 if (mIsRegToService) { 47 NotifyStoppedPlaying(); 48 } 49 } 50 51 NS_IMETHODIMP 52 AudioChannelAgent::Init(mozIDOMWindow* aWindow, 53 nsIAudioChannelAgentCallback* aCallback) { 54 return InitInternal(nsPIDOMWindowInner::From(aWindow), aCallback, 55 /* useWeakRef = */ false); 56 } 57 58 NS_IMETHODIMP 59 AudioChannelAgent::InitWithWeakCallback( 60 mozIDOMWindow* aWindow, nsIAudioChannelAgentCallback* aCallback) { 61 return InitInternal(nsPIDOMWindowInner::From(aWindow), aCallback, 62 /* useWeakRef = */ true); 63 } 64 65 nsresult AudioChannelAgent::FindCorrectWindow(nsPIDOMWindowInner* aWindow) { 66 mWindow = aWindow->GetInProcessScriptableTop(); 67 if (NS_WARN_IF(!mWindow)) { 68 return NS_OK; 69 } 70 71 // From here we do an hack for nested iframes. 72 // The system app doesn't have access to the nested iframe objects so it 73 // cannot control the volume of the agents running in nested apps. What we do 74 // here is to assign those Agents to the top scriptable window of the parent 75 // iframe (what is controlled by the system app). 76 // For doing this we go recursively back into the chain of windows until we 77 // find apps that are not the system one. 78 nsCOMPtr<nsPIDOMWindowOuter> outerParent = mWindow->GetInProcessParent(); 79 if (!outerParent || outerParent == mWindow) { 80 return NS_OK; 81 } 82 83 nsCOMPtr<nsPIDOMWindowInner> parent = outerParent->GetCurrentInnerWindow(); 84 if (!parent) { 85 return NS_OK; 86 } 87 88 nsCOMPtr<Document> doc = parent->GetExtantDoc(); 89 if (!doc) { 90 return NS_OK; 91 } 92 93 if (nsContentUtils::IsChromeDoc(doc)) { 94 return NS_OK; 95 } 96 97 return FindCorrectWindow(parent); 98 } 99 100 nsresult AudioChannelAgent::InitInternal( 101 nsPIDOMWindowInner* aWindow, nsIAudioChannelAgentCallback* aCallback, 102 bool aUseWeakRef) { 103 if (NS_WARN_IF(!aWindow)) { 104 return NS_ERROR_FAILURE; 105 } 106 107 mInnerWindowID = aWindow->WindowID(); 108 109 nsresult rv = FindCorrectWindow(aWindow); 110 if (NS_WARN_IF(NS_FAILED(rv))) { 111 return rv; 112 } 113 114 if (aUseWeakRef) { 115 mWeakCallback = do_GetWeakReference(aCallback); 116 } else { 117 mCallback = aCallback; 118 } 119 120 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 121 ("AudioChannelAgent, InitInternal, this = %p, " 122 "owner = %p, hasCallback = %d\n", 123 this, mWindow.get(), (!!mCallback || !!mWeakCallback))); 124 125 return NS_OK; 126 } 127 128 void AudioChannelAgent::PullInitialUpdate() { 129 RefPtr<AudioChannelService> service = AudioChannelService::Get(); 130 MOZ_ASSERT(service); 131 MOZ_ASSERT(mIsRegToService); 132 133 AudioPlaybackConfig config = service->GetMediaConfig(mWindow); 134 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 135 ("AudioChannelAgent, PullInitialUpdate, this=%p, " 136 "mute=%s, volume=%f, suspend=%s, audioCapturing=%s\n", 137 this, config.mMuted ? "true" : "false", config.mVolume, 138 SuspendTypeToStr(config.mSuspend), 139 config.mCapturedAudio ? "true" : "false")); 140 WindowVolumeChanged(config.mVolume, config.mMuted); 141 WindowSuspendChanged(config.mSuspend); 142 WindowAudioCaptureChanged(InnerWindowID(), config.mCapturedAudio); 143 } 144 145 NS_IMETHODIMP 146 AudioChannelAgent::NotifyStartedPlaying(uint8_t aAudible) { 147 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); 148 if (service == nullptr || mIsRegToService) { 149 return NS_ERROR_FAILURE; 150 } 151 152 MOZ_ASSERT(AudioChannelService::AudibleState::eNotAudible == 0 && 153 AudioChannelService::AudibleState::eMaybeAudible == 1 && 154 AudioChannelService::AudibleState::eAudible == 2); 155 service->RegisterAudioChannelAgent( 156 this, static_cast<AudioChannelService::AudibleState>(aAudible)); 157 158 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 159 ("AudioChannelAgent, NotifyStartedPlaying, this = %p, audible = %s\n", 160 this, 161 AudioChannelService::EnumValueToString( 162 static_cast<AudioChannelService::AudibleState>(aAudible)))); 163 164 mIsRegToService = true; 165 return NS_OK; 166 } 167 168 NS_IMETHODIMP 169 AudioChannelAgent::NotifyStoppedPlaying() { 170 if (!mIsRegToService) { 171 return NS_ERROR_FAILURE; 172 } 173 174 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 175 ("AudioChannelAgent, NotifyStoppedPlaying, this = %p\n", this)); 176 177 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); 178 if (service) { 179 service->UnregisterAudioChannelAgent(this); 180 } 181 182 mIsRegToService = false; 183 return NS_OK; 184 } 185 186 NS_IMETHODIMP 187 AudioChannelAgent::NotifyStartedAudible(uint8_t aAudible, uint32_t aReason) { 188 MOZ_LOG( 189 AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 190 ("AudioChannelAgent, NotifyStartedAudible, this = %p, " 191 "audible = %s, reason = %s\n", 192 this, 193 AudioChannelService::EnumValueToString( 194 static_cast<AudioChannelService::AudibleState>(aAudible)), 195 AudioChannelService::EnumValueToString( 196 static_cast<AudioChannelService::AudibleChangedReasons>(aReason)))); 197 198 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); 199 if (NS_WARN_IF(!service)) { 200 return NS_ERROR_FAILURE; 201 } 202 203 service->AudioAudibleChanged( 204 this, static_cast<AudioChannelService::AudibleState>(aAudible), 205 static_cast<AudioChannelService::AudibleChangedReasons>(aReason)); 206 return NS_OK; 207 } 208 209 already_AddRefed<nsIAudioChannelAgentCallback> 210 AudioChannelAgent::GetCallback() { 211 nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback; 212 if (!callback) { 213 callback = do_QueryReferent(mWeakCallback); 214 } 215 return callback.forget(); 216 } 217 218 void AudioChannelAgent::WindowVolumeChanged(float aVolume, bool aMuted) { 219 nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback(); 220 if (!callback) { 221 return; 222 } 223 224 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 225 ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %s, " 226 "volume = %f\n", 227 this, aMuted ? "true" : "false", aVolume)); 228 callback->WindowVolumeChanged(aVolume, aMuted); 229 } 230 231 void AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend) { 232 nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback(); 233 if (!callback) { 234 return; 235 } 236 237 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 238 ("AudioChannelAgent, WindowSuspendChanged, this = %p, " 239 "suspended = %s\n", 240 this, SuspendTypeToStr(aSuspend))); 241 callback->WindowSuspendChanged(aSuspend); 242 } 243 244 AudioPlaybackConfig AudioChannelAgent::GetMediaConfig() const { 245 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); 246 AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED); 247 if (service) { 248 config = service->GetMediaConfig(mWindow); 249 } 250 return config; 251 } 252 253 uint64_t AudioChannelAgent::WindowID() const { 254 return mWindow ? mWindow->WindowID() : 0; 255 } 256 257 uint64_t AudioChannelAgent::InnerWindowID() const { return mInnerWindowID; } 258 259 void AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID, 260 bool aCapture) { 261 if (aInnerWindowID != mInnerWindowID) { 262 return; 263 } 264 265 nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback(); 266 if (!callback) { 267 return; 268 } 269 270 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, 271 ("AudioChannelAgent, WindowAudioCaptureChanged, this = %p, " 272 "capture = %d\n", 273 this, aCapture)); 274 275 callback->WindowAudioCaptureChanged(aCapture); 276 } 277 278 bool AudioChannelAgent::IsWindowAudioCapturingEnabled() const { 279 return GetMediaConfig().mCapturedAudio; 280 } 281 282 bool AudioChannelAgent::IsPlayingStarted() const { return mIsRegToService; }