Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 5 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "AudioChannelService.h" |
michael@0 | 8 | #include "AudioChannelServiceChild.h" |
michael@0 | 9 | |
michael@0 | 10 | #include "base/basictypes.h" |
michael@0 | 11 | |
michael@0 | 12 | #include "mozilla/Services.h" |
michael@0 | 13 | #include "mozilla/StaticPtr.h" |
michael@0 | 14 | #include "mozilla/unused.h" |
michael@0 | 15 | |
michael@0 | 16 | #include "mozilla/dom/ContentParent.h" |
michael@0 | 17 | |
michael@0 | 18 | #include "nsThreadUtils.h" |
michael@0 | 19 | #include "nsHashPropertyBag.h" |
michael@0 | 20 | #include "nsComponentManagerUtils.h" |
michael@0 | 21 | #include "nsPIDOMWindow.h" |
michael@0 | 22 | #include "nsServiceManagerUtils.h" |
michael@0 | 23 | |
michael@0 | 24 | #ifdef MOZ_WIDGET_GONK |
michael@0 | 25 | #include "nsJSUtils.h" |
michael@0 | 26 | #include "nsCxPusher.h" |
michael@0 | 27 | #include "nsIAudioManager.h" |
michael@0 | 28 | #include "SpeakerManagerService.h" |
michael@0 | 29 | #define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1" |
michael@0 | 30 | #endif |
michael@0 | 31 | |
michael@0 | 32 | #include "mozilla/Preferences.h" |
michael@0 | 33 | |
michael@0 | 34 | using namespace mozilla; |
michael@0 | 35 | using namespace mozilla::dom; |
michael@0 | 36 | using namespace mozilla::hal; |
michael@0 | 37 | |
michael@0 | 38 | StaticRefPtr<AudioChannelService> gAudioChannelService; |
michael@0 | 39 | |
michael@0 | 40 | // Mappings from 'mozaudiochannel' attribute strings to an enumeration. |
michael@0 | 41 | static const nsAttrValue::EnumTable kMozAudioChannelAttributeTable[] = { |
michael@0 | 42 | { "normal", (int16_t)AudioChannel::Normal }, |
michael@0 | 43 | { "content", (int16_t)AudioChannel::Content }, |
michael@0 | 44 | { "notification", (int16_t)AudioChannel::Notification }, |
michael@0 | 45 | { "alarm", (int16_t)AudioChannel::Alarm }, |
michael@0 | 46 | { "telephony", (int16_t)AudioChannel::Telephony }, |
michael@0 | 47 | { "ringer", (int16_t)AudioChannel::Ringer }, |
michael@0 | 48 | { "publicnotification", (int16_t)AudioChannel::Publicnotification }, |
michael@0 | 49 | { nullptr } |
michael@0 | 50 | }; |
michael@0 | 51 | |
michael@0 | 52 | // static |
michael@0 | 53 | AudioChannelService* |
michael@0 | 54 | AudioChannelService::GetAudioChannelService() |
michael@0 | 55 | { |
michael@0 | 56 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 57 | |
michael@0 | 58 | if (XRE_GetProcessType() != GeckoProcessType_Default) { |
michael@0 | 59 | return AudioChannelServiceChild::GetAudioChannelService(); |
michael@0 | 60 | } |
michael@0 | 61 | |
michael@0 | 62 | // If we already exist, exit early |
michael@0 | 63 | if (gAudioChannelService) { |
michael@0 | 64 | return gAudioChannelService; |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | // Create new instance, register, return |
michael@0 | 68 | nsRefPtr<AudioChannelService> service = new AudioChannelService(); |
michael@0 | 69 | NS_ENSURE_TRUE(service, nullptr); |
michael@0 | 70 | |
michael@0 | 71 | gAudioChannelService = service; |
michael@0 | 72 | return gAudioChannelService; |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | void |
michael@0 | 76 | AudioChannelService::Shutdown() |
michael@0 | 77 | { |
michael@0 | 78 | if (XRE_GetProcessType() != GeckoProcessType_Default) { |
michael@0 | 79 | return AudioChannelServiceChild::Shutdown(); |
michael@0 | 80 | } |
michael@0 | 81 | |
michael@0 | 82 | if (gAudioChannelService) { |
michael@0 | 83 | gAudioChannelService = nullptr; |
michael@0 | 84 | } |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | NS_IMPL_ISUPPORTS(AudioChannelService, nsIObserver, nsITimerCallback) |
michael@0 | 88 | |
michael@0 | 89 | AudioChannelService::AudioChannelService() |
michael@0 | 90 | : mCurrentHigherChannel(-1) |
michael@0 | 91 | , mCurrentVisibleHigherChannel(-1) |
michael@0 | 92 | , mPlayableHiddenContentChildID(CONTENT_PROCESS_ID_UNKNOWN) |
michael@0 | 93 | , mDisabled(false) |
michael@0 | 94 | , mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN) |
michael@0 | 95 | { |
michael@0 | 96 | if (XRE_GetProcessType() == GeckoProcessType_Default) { |
michael@0 | 97 | nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
michael@0 | 98 | if (obs) { |
michael@0 | 99 | obs->AddObserver(this, "ipc:content-shutdown", false); |
michael@0 | 100 | obs->AddObserver(this, "xpcom-shutdown", false); |
michael@0 | 101 | #ifdef MOZ_WIDGET_GONK |
michael@0 | 102 | // To monitor the volume settings based on audio channel. |
michael@0 | 103 | obs->AddObserver(this, "mozsettings-changed", false); |
michael@0 | 104 | #endif |
michael@0 | 105 | } |
michael@0 | 106 | } |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | AudioChannelService::~AudioChannelService() |
michael@0 | 110 | { |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | void |
michael@0 | 114 | AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent, |
michael@0 | 115 | AudioChannel aChannel, |
michael@0 | 116 | bool aWithVideo) |
michael@0 | 117 | { |
michael@0 | 118 | if (mDisabled) { |
michael@0 | 119 | return; |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | AudioChannelAgentData* data = new AudioChannelAgentData(aChannel, |
michael@0 | 123 | true /* aElementHidden */, |
michael@0 | 124 | AUDIO_CHANNEL_STATE_MUTED /* aState */, |
michael@0 | 125 | aWithVideo); |
michael@0 | 126 | mAgents.Put(aAgent, data); |
michael@0 | 127 | RegisterType(aChannel, CONTENT_PROCESS_ID_MAIN, aWithVideo); |
michael@0 | 128 | |
michael@0 | 129 | // If this is the first agent for this window, we must notify the observers. |
michael@0 | 130 | uint32_t count = CountWindow(aAgent->Window()); |
michael@0 | 131 | if (count == 1) { |
michael@0 | 132 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 133 | services::GetObserverService(); |
michael@0 | 134 | if (observerService) { |
michael@0 | 135 | observerService->NotifyObservers(ToSupports(aAgent->Window()), |
michael@0 | 136 | "media-playback", |
michael@0 | 137 | NS_LITERAL_STRING("active").get()); |
michael@0 | 138 | } |
michael@0 | 139 | } |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | void |
michael@0 | 143 | AudioChannelService::RegisterType(AudioChannel aChannel, uint64_t aChildID, |
michael@0 | 144 | bool aWithVideo) |
michael@0 | 145 | { |
michael@0 | 146 | if (mDisabled) { |
michael@0 | 147 | return; |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | AudioChannelInternalType type = GetInternalType(aChannel, true); |
michael@0 | 151 | mChannelCounters[type].AppendElement(aChildID); |
michael@0 | 152 | |
michael@0 | 153 | if (XRE_GetProcessType() == GeckoProcessType_Default) { |
michael@0 | 154 | // Since there is another telephony registered, we can unregister old one |
michael@0 | 155 | // immediately. |
michael@0 | 156 | if (mDeferTelChannelTimer && aChannel == AudioChannel::Telephony) { |
michael@0 | 157 | mDeferTelChannelTimer->Cancel(); |
michael@0 | 158 | mDeferTelChannelTimer = nullptr; |
michael@0 | 159 | UnregisterTypeInternal(aChannel, mTimerElementHidden, mTimerChildID, |
michael@0 | 160 | false); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | if (aWithVideo) { |
michael@0 | 164 | mWithVideoChildIDs.AppendElement(aChildID); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | // No hidden content channel can be playable if there is a content channel |
michael@0 | 168 | // in foreground (bug 855208), nor if there is a normal channel with video |
michael@0 | 169 | // in foreground (bug 894249). |
michael@0 | 170 | if (type == AUDIO_CHANNEL_INT_CONTENT || |
michael@0 | 171 | (type == AUDIO_CHANNEL_INT_NORMAL && |
michael@0 | 172 | mWithVideoChildIDs.Contains(aChildID))) { |
michael@0 | 173 | mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN; |
michael@0 | 174 | } |
michael@0 | 175 | // One hidden content channel can be playable only when there is no any |
michael@0 | 176 | // content channel in the foreground, and no normal channel with video in |
michael@0 | 177 | // foreground. |
michael@0 | 178 | else if (type == AUDIO_CHANNEL_INT_CONTENT_HIDDEN && |
michael@0 | 179 | mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) { |
michael@0 | 180 | mPlayableHiddenContentChildID = aChildID; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | // In order to avoid race conditions, it's safer to notify any existing |
michael@0 | 184 | // agent any time a new one is registered. |
michael@0 | 185 | SendAudioChannelChangedNotification(aChildID); |
michael@0 | 186 | Notify(); |
michael@0 | 187 | } |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | void |
michael@0 | 191 | AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) |
michael@0 | 192 | { |
michael@0 | 193 | if (mDisabled) { |
michael@0 | 194 | return; |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | nsAutoPtr<AudioChannelAgentData> data; |
michael@0 | 198 | mAgents.RemoveAndForget(aAgent, data); |
michael@0 | 199 | |
michael@0 | 200 | if (data) { |
michael@0 | 201 | UnregisterType(data->mChannel, data->mElementHidden, |
michael@0 | 202 | CONTENT_PROCESS_ID_MAIN, data->mWithVideo); |
michael@0 | 203 | } |
michael@0 | 204 | #ifdef MOZ_WIDGET_GONK |
michael@0 | 205 | bool active = AnyAudioChannelIsActive(); |
michael@0 | 206 | for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) { |
michael@0 | 207 | mSpeakerManager[i]->SetAudioChannelActive(active); |
michael@0 | 208 | } |
michael@0 | 209 | #endif |
michael@0 | 210 | |
michael@0 | 211 | // If this is the last agent for this window, we must notify the observers. |
michael@0 | 212 | uint32_t count = CountWindow(aAgent->Window()); |
michael@0 | 213 | if (count == 0) { |
michael@0 | 214 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 215 | services::GetObserverService(); |
michael@0 | 216 | if (observerService) { |
michael@0 | 217 | observerService->NotifyObservers(ToSupports(aAgent->Window()), |
michael@0 | 218 | "media-playback", |
michael@0 | 219 | NS_LITERAL_STRING("inactive").get()); |
michael@0 | 220 | } |
michael@0 | 221 | } |
michael@0 | 222 | } |
michael@0 | 223 | |
michael@0 | 224 | void |
michael@0 | 225 | AudioChannelService::UnregisterType(AudioChannel aChannel, |
michael@0 | 226 | bool aElementHidden, |
michael@0 | 227 | uint64_t aChildID, |
michael@0 | 228 | bool aWithVideo) |
michael@0 | 229 | { |
michael@0 | 230 | if (mDisabled) { |
michael@0 | 231 | return; |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | // There are two reasons to defer the decrease of telephony channel. |
michael@0 | 235 | // 1. User can have time to remove device from his ear before music resuming. |
michael@0 | 236 | // 2. Give BT SCO to be disconnected before starting to connect A2DP. |
michael@0 | 237 | if (XRE_GetProcessType() == GeckoProcessType_Default && |
michael@0 | 238 | aChannel == AudioChannel::Telephony && |
michael@0 | 239 | (mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].Length() + |
michael@0 | 240 | mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].Length()) == 1) { |
michael@0 | 241 | mTimerElementHidden = aElementHidden; |
michael@0 | 242 | mTimerChildID = aChildID; |
michael@0 | 243 | mDeferTelChannelTimer = do_CreateInstance("@mozilla.org/timer;1"); |
michael@0 | 244 | mDeferTelChannelTimer->InitWithCallback(this, 1500, nsITimer::TYPE_ONE_SHOT); |
michael@0 | 245 | return; |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | UnregisterTypeInternal(aChannel, aElementHidden, aChildID, aWithVideo); |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | void |
michael@0 | 252 | AudioChannelService::UnregisterTypeInternal(AudioChannel aChannel, |
michael@0 | 253 | bool aElementHidden, |
michael@0 | 254 | uint64_t aChildID, |
michael@0 | 255 | bool aWithVideo) |
michael@0 | 256 | { |
michael@0 | 257 | // The array may contain multiple occurrence of this appId but |
michael@0 | 258 | // this should remove only the first one. |
michael@0 | 259 | AudioChannelInternalType type = GetInternalType(aChannel, aElementHidden); |
michael@0 | 260 | MOZ_ASSERT(mChannelCounters[type].Contains(aChildID)); |
michael@0 | 261 | mChannelCounters[type].RemoveElement(aChildID); |
michael@0 | 262 | |
michael@0 | 263 | // In order to avoid race conditions, it's safer to notify any existing |
michael@0 | 264 | // agent any time a new one is registered. |
michael@0 | 265 | if (XRE_GetProcessType() == GeckoProcessType_Default) { |
michael@0 | 266 | // No hidden content channel is playable if the original playable hidden |
michael@0 | 267 | // process does not need to play audio from background anymore. |
michael@0 | 268 | if (aChannel == AudioChannel::Content && |
michael@0 | 269 | mPlayableHiddenContentChildID == aChildID && |
michael@0 | 270 | !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID)) { |
michael@0 | 271 | mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN; |
michael@0 | 272 | } |
michael@0 | 273 | |
michael@0 | 274 | if (aWithVideo) { |
michael@0 | 275 | MOZ_ASSERT(mWithVideoChildIDs.Contains(aChildID)); |
michael@0 | 276 | mWithVideoChildIDs.RemoveElement(aChildID); |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | SendAudioChannelChangedNotification(aChildID); |
michael@0 | 280 | Notify(); |
michael@0 | 281 | } |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | void |
michael@0 | 285 | AudioChannelService::UpdateChannelType(AudioChannel aChannel, |
michael@0 | 286 | uint64_t aChildID, |
michael@0 | 287 | bool aElementHidden, |
michael@0 | 288 | bool aElementWasHidden) |
michael@0 | 289 | { |
michael@0 | 290 | // Calculate the new and old internal type and update the hashtable if needed. |
michael@0 | 291 | AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden); |
michael@0 | 292 | AudioChannelInternalType oldType = GetInternalType(aChannel, aElementWasHidden); |
michael@0 | 293 | |
michael@0 | 294 | if (newType != oldType) { |
michael@0 | 295 | mChannelCounters[newType].AppendElement(aChildID); |
michael@0 | 296 | MOZ_ASSERT(mChannelCounters[oldType].Contains(aChildID)); |
michael@0 | 297 | mChannelCounters[oldType].RemoveElement(aChildID); |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | // No hidden content channel can be playable if there is a content channel |
michael@0 | 301 | // in foreground (bug 855208), nor if there is a normal channel with video |
michael@0 | 302 | // in foreground (bug 894249). |
michael@0 | 303 | if (newType == AUDIO_CHANNEL_INT_CONTENT || |
michael@0 | 304 | (newType == AUDIO_CHANNEL_INT_NORMAL && |
michael@0 | 305 | mWithVideoChildIDs.Contains(aChildID))) { |
michael@0 | 306 | mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN; |
michael@0 | 307 | } |
michael@0 | 308 | // If there is no content channel in foreground and no normal channel with |
michael@0 | 309 | // video in foreground, the last content channel which goes from foreground |
michael@0 | 310 | // to background can be playable. |
michael@0 | 311 | else if (oldType == AUDIO_CHANNEL_INT_CONTENT && |
michael@0 | 312 | newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN && |
michael@0 | 313 | mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) { |
michael@0 | 314 | mPlayableHiddenContentChildID = aChildID; |
michael@0 | 315 | } |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | AudioChannelState |
michael@0 | 319 | AudioChannelService::GetState(AudioChannelAgent* aAgent, bool aElementHidden) |
michael@0 | 320 | { |
michael@0 | 321 | AudioChannelAgentData* data; |
michael@0 | 322 | if (!mAgents.Get(aAgent, &data)) { |
michael@0 | 323 | return AUDIO_CHANNEL_STATE_MUTED; |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | bool oldElementHidden = data->mElementHidden; |
michael@0 | 327 | // Update visibility. |
michael@0 | 328 | data->mElementHidden = aElementHidden; |
michael@0 | 329 | |
michael@0 | 330 | data->mState = GetStateInternal(data->mChannel, CONTENT_PROCESS_ID_MAIN, |
michael@0 | 331 | aElementHidden, oldElementHidden); |
michael@0 | 332 | return data->mState; |
michael@0 | 333 | } |
michael@0 | 334 | |
michael@0 | 335 | AudioChannelState |
michael@0 | 336 | AudioChannelService::GetStateInternal(AudioChannel aChannel, uint64_t aChildID, |
michael@0 | 337 | bool aElementHidden, |
michael@0 | 338 | bool aElementWasHidden) |
michael@0 | 339 | { |
michael@0 | 340 | UpdateChannelType(aChannel, aChildID, aElementHidden, aElementWasHidden); |
michael@0 | 341 | |
michael@0 | 342 | // Calculating the new and old type and update the hashtable if needed. |
michael@0 | 343 | AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden); |
michael@0 | 344 | AudioChannelInternalType oldType = GetInternalType(aChannel, |
michael@0 | 345 | aElementWasHidden); |
michael@0 | 346 | |
michael@0 | 347 | if (newType != oldType && |
michael@0 | 348 | (aChannel == AudioChannel::Content || |
michael@0 | 349 | (aChannel == AudioChannel::Normal && |
michael@0 | 350 | mWithVideoChildIDs.Contains(aChildID)))) { |
michael@0 | 351 | Notify(); |
michael@0 | 352 | } |
michael@0 | 353 | |
michael@0 | 354 | SendAudioChannelChangedNotification(aChildID); |
michael@0 | 355 | |
michael@0 | 356 | // Let play any visible audio channel. |
michael@0 | 357 | if (!aElementHidden) { |
michael@0 | 358 | if (CheckVolumeFadedCondition(newType, aElementHidden)) { |
michael@0 | 359 | return AUDIO_CHANNEL_STATE_FADED; |
michael@0 | 360 | } |
michael@0 | 361 | return AUDIO_CHANNEL_STATE_NORMAL; |
michael@0 | 362 | } |
michael@0 | 363 | |
michael@0 | 364 | // We are not visible, maybe we have to mute. |
michael@0 | 365 | if (newType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN || |
michael@0 | 366 | (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN && |
michael@0 | 367 | // One process can have multiple content channels; and during the |
michael@0 | 368 | // transition from foreground to background, its content channels will be |
michael@0 | 369 | // updated with correct visibility status one by one. All its content |
michael@0 | 370 | // channels should remain playable until all of their visibility statuses |
michael@0 | 371 | // have been updated as hidden. After all its content channels have been |
michael@0 | 372 | // updated properly as hidden, mPlayableHiddenContentChildID is used to |
michael@0 | 373 | // check whether this background process is playable or not. |
michael@0 | 374 | !(mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) || |
michael@0 | 375 | (mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() && |
michael@0 | 376 | mPlayableHiddenContentChildID == aChildID)))) { |
michael@0 | 377 | return AUDIO_CHANNEL_STATE_MUTED; |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | // After checking the condition on normal & content channel, if the state |
michael@0 | 381 | // is not on muted then checking other higher channels type here. |
michael@0 | 382 | if (ChannelsActiveWithHigherPriorityThan(newType)) { |
michael@0 | 383 | MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN); |
michael@0 | 384 | if (CheckVolumeFadedCondition(newType, aElementHidden)) { |
michael@0 | 385 | return AUDIO_CHANNEL_STATE_FADED; |
michael@0 | 386 | } |
michael@0 | 387 | return AUDIO_CHANNEL_STATE_MUTED; |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | return AUDIO_CHANNEL_STATE_NORMAL; |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | bool |
michael@0 | 394 | AudioChannelService::CheckVolumeFadedCondition(AudioChannelInternalType aType, |
michael@0 | 395 | bool aElementHidden) |
michael@0 | 396 | { |
michael@0 | 397 | // Only normal & content channels are considered |
michael@0 | 398 | if (aType > AUDIO_CHANNEL_INT_CONTENT_HIDDEN) { |
michael@0 | 399 | return false; |
michael@0 | 400 | } |
michael@0 | 401 | |
michael@0 | 402 | // Consider that audio from notification is with short duration |
michael@0 | 403 | // so just fade the volume not pause it |
michael@0 | 404 | if (mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION].IsEmpty() && |
michael@0 | 405 | mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) { |
michael@0 | 406 | return false; |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | // Since this element is on the foreground, it can be allowed to play always. |
michael@0 | 410 | // So return true directly when there is any notification channel alive. |
michael@0 | 411 | if (aElementHidden == false) { |
michael@0 | 412 | return true; |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | // If element is on the background, it is possible paused by channels higher |
michael@0 | 416 | // then notification. |
michael@0 | 417 | for (int i = AUDIO_CHANNEL_INT_LAST - 1; |
michael@0 | 418 | i != AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN; --i) { |
michael@0 | 419 | if (!mChannelCounters[i].IsEmpty()) { |
michael@0 | 420 | return false; |
michael@0 | 421 | } |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | return true; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | bool |
michael@0 | 428 | AudioChannelService::ContentOrNormalChannelIsActive() |
michael@0 | 429 | { |
michael@0 | 430 | return !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() || |
michael@0 | 431 | !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].IsEmpty() || |
michael@0 | 432 | !mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty(); |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | bool |
michael@0 | 436 | AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID) |
michael@0 | 437 | { |
michael@0 | 438 | return mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) || |
michael@0 | 439 | mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID) || |
michael@0 | 440 | mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].Contains(aChildID); |
michael@0 | 441 | } |
michael@0 | 442 | |
michael@0 | 443 | void |
michael@0 | 444 | AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel, |
michael@0 | 445 | bool aHidden) |
michael@0 | 446 | { |
michael@0 | 447 | SetDefaultVolumeControlChannelInternal(aChannel, aHidden, |
michael@0 | 448 | CONTENT_PROCESS_ID_MAIN); |
michael@0 | 449 | } |
michael@0 | 450 | |
michael@0 | 451 | void |
michael@0 | 452 | AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel, |
michael@0 | 453 | bool aHidden, |
michael@0 | 454 | uint64_t aChildID) |
michael@0 | 455 | { |
michael@0 | 456 | if (XRE_GetProcessType() != GeckoProcessType_Default) { |
michael@0 | 457 | return; |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | // If this child is in the background and mDefChannelChildID is set to |
michael@0 | 461 | // others then it means other child in the foreground already set it's |
michael@0 | 462 | // own default channel already. |
michael@0 | 463 | if (!aHidden && mDefChannelChildID != aChildID) { |
michael@0 | 464 | return; |
michael@0 | 465 | } |
michael@0 | 466 | |
michael@0 | 467 | mDefChannelChildID = aChildID; |
michael@0 | 468 | nsString channelName; |
michael@0 | 469 | |
michael@0 | 470 | if (aChannel == -1) { |
michael@0 | 471 | channelName.AssignASCII("unknown"); |
michael@0 | 472 | } else { |
michael@0 | 473 | GetAudioChannelString(static_cast<AudioChannel>(aChannel), channelName); |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
michael@0 | 477 | if (obs) { |
michael@0 | 478 | obs->NotifyObservers(nullptr, "default-volume-channel-changed", |
michael@0 | 479 | channelName.get()); |
michael@0 | 480 | } |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | void |
michael@0 | 484 | AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID) |
michael@0 | 485 | { |
michael@0 | 486 | if (XRE_GetProcessType() != GeckoProcessType_Default) { |
michael@0 | 487 | return; |
michael@0 | 488 | } |
michael@0 | 489 | |
michael@0 | 490 | nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); |
michael@0 | 491 | props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), aChildID); |
michael@0 | 492 | |
michael@0 | 493 | nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
michael@0 | 494 | if (obs) { |
michael@0 | 495 | obs->NotifyObservers(static_cast<nsIWritablePropertyBag*>(props), |
michael@0 | 496 | "audio-channel-process-changed", nullptr); |
michael@0 | 497 | } |
michael@0 | 498 | |
michael@0 | 499 | // Calculating the most important active channel. |
michael@0 | 500 | int32_t higher = -1; |
michael@0 | 501 | |
michael@0 | 502 | // Top-Down in the hierarchy for visible elements |
michael@0 | 503 | if (!mChannelCounters[AUDIO_CHANNEL_INT_PUBLICNOTIFICATION].IsEmpty()) { |
michael@0 | 504 | higher = static_cast<int32_t>(AudioChannel::Publicnotification); |
michael@0 | 505 | } |
michael@0 | 506 | |
michael@0 | 507 | else if (!mChannelCounters[AUDIO_CHANNEL_INT_RINGER].IsEmpty()) { |
michael@0 | 508 | higher = static_cast<int32_t>(AudioChannel::Ringer); |
michael@0 | 509 | } |
michael@0 | 510 | |
michael@0 | 511 | else if (!mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].IsEmpty()) { |
michael@0 | 512 | higher = static_cast<int32_t>(AudioChannel::Telephony); |
michael@0 | 513 | } |
michael@0 | 514 | |
michael@0 | 515 | else if (!mChannelCounters[AUDIO_CHANNEL_INT_ALARM].IsEmpty()) { |
michael@0 | 516 | higher = static_cast<int32_t>(AudioChannel::Alarm); |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION].IsEmpty()) { |
michael@0 | 520 | higher = static_cast<int32_t>(AudioChannel::Notification); |
michael@0 | 521 | } |
michael@0 | 522 | |
michael@0 | 523 | else if (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) { |
michael@0 | 524 | higher = static_cast<int32_t>(AudioChannel::Content); |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | else if (!mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty()) { |
michael@0 | 528 | higher = static_cast<int32_t>(AudioChannel::Normal); |
michael@0 | 529 | } |
michael@0 | 530 | |
michael@0 | 531 | int32_t visibleHigher = higher; |
michael@0 | 532 | |
michael@0 | 533 | // Top-Down in the hierarchy for non-visible elements |
michael@0 | 534 | // And we can ignore normal channel because it can't play in the background. |
michael@0 | 535 | int32_t index; |
michael@0 | 536 | for (index = 0; kMozAudioChannelAttributeTable[index].tag; ++index); |
michael@0 | 537 | |
michael@0 | 538 | for (--index; |
michael@0 | 539 | kMozAudioChannelAttributeTable[index].value > higher && |
michael@0 | 540 | kMozAudioChannelAttributeTable[index].value > (int16_t)AudioChannel::Normal; |
michael@0 | 541 | --index) { |
michael@0 | 542 | if (kMozAudioChannelAttributeTable[index].value == (int16_t)AudioChannel::Content && |
michael@0 | 543 | mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) { |
michael@0 | 544 | higher = kMozAudioChannelAttributeTable[index].value; |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | // Each channel type will be split to fg and bg for recording the state, |
michael@0 | 548 | // so here need to do a translation. |
michael@0 | 549 | if (!mChannelCounters[index * 2 + 1].IsEmpty()) { |
michael@0 | 550 | higher = kMozAudioChannelAttributeTable[index].value; |
michael@0 | 551 | break; |
michael@0 | 552 | } |
michael@0 | 553 | } |
michael@0 | 554 | |
michael@0 | 555 | if (higher != mCurrentHigherChannel) { |
michael@0 | 556 | mCurrentHigherChannel = higher; |
michael@0 | 557 | |
michael@0 | 558 | nsString channelName; |
michael@0 | 559 | if (mCurrentHigherChannel != -1) { |
michael@0 | 560 | GetAudioChannelString(static_cast<AudioChannel>(mCurrentHigherChannel), |
michael@0 | 561 | channelName); |
michael@0 | 562 | } else { |
michael@0 | 563 | channelName.AssignLiteral("none"); |
michael@0 | 564 | } |
michael@0 | 565 | |
michael@0 | 566 | if (obs) { |
michael@0 | 567 | obs->NotifyObservers(nullptr, "audio-channel-changed", channelName.get()); |
michael@0 | 568 | } |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | if (visibleHigher != mCurrentVisibleHigherChannel) { |
michael@0 | 572 | mCurrentVisibleHigherChannel = visibleHigher; |
michael@0 | 573 | |
michael@0 | 574 | nsString channelName; |
michael@0 | 575 | if (mCurrentVisibleHigherChannel != -1) { |
michael@0 | 576 | GetAudioChannelString(static_cast<AudioChannel>(mCurrentVisibleHigherChannel), |
michael@0 | 577 | channelName); |
michael@0 | 578 | } else { |
michael@0 | 579 | channelName.AssignLiteral("none"); |
michael@0 | 580 | } |
michael@0 | 581 | |
michael@0 | 582 | if (obs) { |
michael@0 | 583 | obs->NotifyObservers(nullptr, "visible-audio-channel-changed", channelName.get()); |
michael@0 | 584 | } |
michael@0 | 585 | } |
michael@0 | 586 | } |
michael@0 | 587 | |
michael@0 | 588 | PLDHashOperator |
michael@0 | 589 | AudioChannelService::NotifyEnumerator(AudioChannelAgent* aAgent, |
michael@0 | 590 | AudioChannelAgentData* aData, void* aUnused) |
michael@0 | 591 | { |
michael@0 | 592 | MOZ_ASSERT(aAgent); |
michael@0 | 593 | aAgent->NotifyAudioChannelStateChanged(); |
michael@0 | 594 | return PL_DHASH_NEXT; |
michael@0 | 595 | } |
michael@0 | 596 | |
michael@0 | 597 | void |
michael@0 | 598 | AudioChannelService::Notify() |
michael@0 | 599 | { |
michael@0 | 600 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 601 | |
michael@0 | 602 | // Notify any agent for the main process. |
michael@0 | 603 | mAgents.EnumerateRead(NotifyEnumerator, nullptr); |
michael@0 | 604 | |
michael@0 | 605 | // Notify for the child processes. |
michael@0 | 606 | nsTArray<ContentParent*> children; |
michael@0 | 607 | ContentParent::GetAll(children); |
michael@0 | 608 | for (uint32_t i = 0; i < children.Length(); i++) { |
michael@0 | 609 | unused << children[i]->SendAudioChannelNotify(); |
michael@0 | 610 | } |
michael@0 | 611 | } |
michael@0 | 612 | |
michael@0 | 613 | NS_IMETHODIMP |
michael@0 | 614 | AudioChannelService::Notify(nsITimer* aTimer) |
michael@0 | 615 | { |
michael@0 | 616 | UnregisterTypeInternal(AudioChannel::Telephony, mTimerElementHidden, |
michael@0 | 617 | mTimerChildID, false); |
michael@0 | 618 | mDeferTelChannelTimer = nullptr; |
michael@0 | 619 | return NS_OK; |
michael@0 | 620 | } |
michael@0 | 621 | |
michael@0 | 622 | bool |
michael@0 | 623 | AudioChannelService::AnyAudioChannelIsActive() |
michael@0 | 624 | { |
michael@0 | 625 | for (int i = AUDIO_CHANNEL_INT_LAST - 1; |
michael@0 | 626 | i >= AUDIO_CHANNEL_INT_NORMAL; --i) { |
michael@0 | 627 | if (!mChannelCounters[i].IsEmpty()) { |
michael@0 | 628 | return true; |
michael@0 | 629 | } |
michael@0 | 630 | } |
michael@0 | 631 | |
michael@0 | 632 | return false; |
michael@0 | 633 | } |
michael@0 | 634 | |
michael@0 | 635 | bool |
michael@0 | 636 | AudioChannelService::ChannelsActiveWithHigherPriorityThan( |
michael@0 | 637 | AudioChannelInternalType aType) |
michael@0 | 638 | { |
michael@0 | 639 | for (int i = AUDIO_CHANNEL_INT_LAST - 1; |
michael@0 | 640 | i != AUDIO_CHANNEL_INT_CONTENT_HIDDEN; --i) { |
michael@0 | 641 | if (i == aType) { |
michael@0 | 642 | return false; |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | if (!mChannelCounters[i].IsEmpty()) { |
michael@0 | 646 | return true; |
michael@0 | 647 | } |
michael@0 | 648 | } |
michael@0 | 649 | |
michael@0 | 650 | return false; |
michael@0 | 651 | } |
michael@0 | 652 | |
michael@0 | 653 | NS_IMETHODIMP |
michael@0 | 654 | AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) |
michael@0 | 655 | { |
michael@0 | 656 | if (!strcmp(aTopic, "xpcom-shutdown")) { |
michael@0 | 657 | mDisabled = true; |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | if (!strcmp(aTopic, "ipc:content-shutdown")) { |
michael@0 | 661 | nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); |
michael@0 | 662 | if (!props) { |
michael@0 | 663 | NS_WARNING("ipc:content-shutdown message without property bag as subject"); |
michael@0 | 664 | return NS_OK; |
michael@0 | 665 | } |
michael@0 | 666 | |
michael@0 | 667 | int32_t index; |
michael@0 | 668 | uint64_t childID = 0; |
michael@0 | 669 | nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), |
michael@0 | 670 | &childID); |
michael@0 | 671 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 672 | for (int32_t type = AUDIO_CHANNEL_INT_NORMAL; |
michael@0 | 673 | type < AUDIO_CHANNEL_INT_LAST; |
michael@0 | 674 | ++type) { |
michael@0 | 675 | |
michael@0 | 676 | while ((index = mChannelCounters[type].IndexOf(childID)) != -1) { |
michael@0 | 677 | mChannelCounters[type].RemoveElementAt(index); |
michael@0 | 678 | } |
michael@0 | 679 | } |
michael@0 | 680 | |
michael@0 | 681 | // No hidden content channel is playable if the original playable hidden |
michael@0 | 682 | // process shuts down. |
michael@0 | 683 | if (mPlayableHiddenContentChildID == childID) { |
michael@0 | 684 | mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN; |
michael@0 | 685 | } |
michael@0 | 686 | |
michael@0 | 687 | while ((index = mWithVideoChildIDs.IndexOf(childID)) != -1) { |
michael@0 | 688 | mWithVideoChildIDs.RemoveElementAt(index); |
michael@0 | 689 | } |
michael@0 | 690 | |
michael@0 | 691 | // We don't have to remove the agents from the mAgents hashtable because if |
michael@0 | 692 | // that table contains only agents running on the same process. |
michael@0 | 693 | |
michael@0 | 694 | SendAudioChannelChangedNotification(childID); |
michael@0 | 695 | Notify(); |
michael@0 | 696 | |
michael@0 | 697 | if (mDefChannelChildID == childID) { |
michael@0 | 698 | SetDefaultVolumeControlChannelInternal(-1, false, childID); |
michael@0 | 699 | mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN; |
michael@0 | 700 | } |
michael@0 | 701 | } else { |
michael@0 | 702 | NS_WARNING("ipc:content-shutdown message without childID property"); |
michael@0 | 703 | } |
michael@0 | 704 | } |
michael@0 | 705 | #ifdef MOZ_WIDGET_GONK |
michael@0 | 706 | // To process the volume control on each audio channel according to |
michael@0 | 707 | // change of settings |
michael@0 | 708 | else if (!strcmp(aTopic, "mozsettings-changed")) { |
michael@0 | 709 | AutoSafeJSContext cx; |
michael@0 | 710 | nsDependentString dataStr(aData); |
michael@0 | 711 | JS::Rooted<JS::Value> val(cx); |
michael@0 | 712 | if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) || |
michael@0 | 713 | !val.isObject()) { |
michael@0 | 714 | return NS_OK; |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | JS::Rooted<JSObject*> obj(cx, &val.toObject()); |
michael@0 | 718 | JS::Rooted<JS::Value> key(cx); |
michael@0 | 719 | if (!JS_GetProperty(cx, obj, "key", &key) || |
michael@0 | 720 | !key.isString()) { |
michael@0 | 721 | return NS_OK; |
michael@0 | 722 | } |
michael@0 | 723 | |
michael@0 | 724 | JS::Rooted<JSString*> jsKey(cx, JS::ToString(cx, key)); |
michael@0 | 725 | if (!jsKey) { |
michael@0 | 726 | return NS_OK; |
michael@0 | 727 | } |
michael@0 | 728 | nsDependentJSString keyStr; |
michael@0 | 729 | if (!keyStr.init(cx, jsKey) || keyStr.Find("audio.volume.", 0, false)) { |
michael@0 | 730 | return NS_OK; |
michael@0 | 731 | } |
michael@0 | 732 | |
michael@0 | 733 | JS::Rooted<JS::Value> value(cx); |
michael@0 | 734 | if (!JS_GetProperty(cx, obj, "value", &value) || !value.isInt32()) { |
michael@0 | 735 | return NS_OK; |
michael@0 | 736 | } |
michael@0 | 737 | |
michael@0 | 738 | nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID); |
michael@0 | 739 | NS_ENSURE_TRUE(audioManager, NS_OK); |
michael@0 | 740 | |
michael@0 | 741 | int32_t index = value.toInt32(); |
michael@0 | 742 | if (keyStr.EqualsLiteral("audio.volume.content")) { |
michael@0 | 743 | audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Content, index); |
michael@0 | 744 | } else if (keyStr.EqualsLiteral("audio.volume.notification")) { |
michael@0 | 745 | audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Notification, index); |
michael@0 | 746 | } else if (keyStr.EqualsLiteral("audio.volume.alarm")) { |
michael@0 | 747 | audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Alarm, index); |
michael@0 | 748 | } else if (keyStr.EqualsLiteral("audio.volume.telephony")) { |
michael@0 | 749 | audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Telephony, index); |
michael@0 | 750 | } else if (!keyStr.EqualsLiteral("audio.volume.bt_sco")) { |
michael@0 | 751 | // bt_sco is not a valid audio channel so we manipulate it in |
michael@0 | 752 | // AudioManager.cpp. And the others should not be used. |
michael@0 | 753 | // We didn't use MOZ_ASSUME_UNREACHABLE here because any web content who |
michael@0 | 754 | // has permission of mozSettings can set any names then it can be easy to |
michael@0 | 755 | // crash the B2G. |
michael@0 | 756 | NS_WARNING("unexpected audio channel for volume control"); |
michael@0 | 757 | } |
michael@0 | 758 | } |
michael@0 | 759 | #endif |
michael@0 | 760 | |
michael@0 | 761 | return NS_OK; |
michael@0 | 762 | } |
michael@0 | 763 | |
michael@0 | 764 | AudioChannelService::AudioChannelInternalType |
michael@0 | 765 | AudioChannelService::GetInternalType(AudioChannel aChannel, |
michael@0 | 766 | bool aElementHidden) |
michael@0 | 767 | { |
michael@0 | 768 | switch (aChannel) { |
michael@0 | 769 | case AudioChannel::Normal: |
michael@0 | 770 | return aElementHidden |
michael@0 | 771 | ? AUDIO_CHANNEL_INT_NORMAL_HIDDEN |
michael@0 | 772 | : AUDIO_CHANNEL_INT_NORMAL; |
michael@0 | 773 | |
michael@0 | 774 | case AudioChannel::Content: |
michael@0 | 775 | return aElementHidden |
michael@0 | 776 | ? AUDIO_CHANNEL_INT_CONTENT_HIDDEN |
michael@0 | 777 | : AUDIO_CHANNEL_INT_CONTENT; |
michael@0 | 778 | |
michael@0 | 779 | case AudioChannel::Notification: |
michael@0 | 780 | return aElementHidden |
michael@0 | 781 | ? AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN |
michael@0 | 782 | : AUDIO_CHANNEL_INT_NOTIFICATION; |
michael@0 | 783 | |
michael@0 | 784 | case AudioChannel::Alarm: |
michael@0 | 785 | return aElementHidden |
michael@0 | 786 | ? AUDIO_CHANNEL_INT_ALARM_HIDDEN |
michael@0 | 787 | : AUDIO_CHANNEL_INT_ALARM; |
michael@0 | 788 | |
michael@0 | 789 | case AudioChannel::Telephony: |
michael@0 | 790 | return aElementHidden |
michael@0 | 791 | ? AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN |
michael@0 | 792 | : AUDIO_CHANNEL_INT_TELEPHONY; |
michael@0 | 793 | |
michael@0 | 794 | case AudioChannel::Ringer: |
michael@0 | 795 | return aElementHidden |
michael@0 | 796 | ? AUDIO_CHANNEL_INT_RINGER_HIDDEN |
michael@0 | 797 | : AUDIO_CHANNEL_INT_RINGER; |
michael@0 | 798 | |
michael@0 | 799 | case AudioChannel::Publicnotification: |
michael@0 | 800 | return aElementHidden |
michael@0 | 801 | ? AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN |
michael@0 | 802 | : AUDIO_CHANNEL_INT_PUBLICNOTIFICATION; |
michael@0 | 803 | |
michael@0 | 804 | default: |
michael@0 | 805 | break; |
michael@0 | 806 | } |
michael@0 | 807 | |
michael@0 | 808 | MOZ_CRASH("unexpected audio channel"); |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | struct RefreshAgentsVolumeData |
michael@0 | 812 | { |
michael@0 | 813 | RefreshAgentsVolumeData(nsPIDOMWindow* aWindow) |
michael@0 | 814 | : mWindow(aWindow) |
michael@0 | 815 | {} |
michael@0 | 816 | |
michael@0 | 817 | nsPIDOMWindow* mWindow; |
michael@0 | 818 | nsTArray<nsRefPtr<AudioChannelAgent>> mAgents; |
michael@0 | 819 | }; |
michael@0 | 820 | |
michael@0 | 821 | PLDHashOperator |
michael@0 | 822 | AudioChannelService::RefreshAgentsVolumeEnumerator(AudioChannelAgent* aAgent, |
michael@0 | 823 | AudioChannelAgentData* aUnused, |
michael@0 | 824 | void* aPtr) |
michael@0 | 825 | { |
michael@0 | 826 | MOZ_ASSERT(aAgent); |
michael@0 | 827 | RefreshAgentsVolumeData* data = static_cast<RefreshAgentsVolumeData*>(aPtr); |
michael@0 | 828 | MOZ_ASSERT(data); |
michael@0 | 829 | |
michael@0 | 830 | nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aAgent->Window()); |
michael@0 | 831 | if (window && !window->IsInnerWindow()) { |
michael@0 | 832 | window = window->GetCurrentInnerWindow(); |
michael@0 | 833 | } |
michael@0 | 834 | |
michael@0 | 835 | if (window == data->mWindow) { |
michael@0 | 836 | data->mAgents.AppendElement(aAgent); |
michael@0 | 837 | } |
michael@0 | 838 | |
michael@0 | 839 | return PL_DHASH_NEXT; |
michael@0 | 840 | } |
michael@0 | 841 | void |
michael@0 | 842 | AudioChannelService::RefreshAgentsVolume(nsPIDOMWindow* aWindow) |
michael@0 | 843 | { |
michael@0 | 844 | RefreshAgentsVolumeData data(aWindow); |
michael@0 | 845 | mAgents.EnumerateRead(RefreshAgentsVolumeEnumerator, &data); |
michael@0 | 846 | |
michael@0 | 847 | for (uint32_t i = 0; i < data.mAgents.Length(); ++i) { |
michael@0 | 848 | data.mAgents[i]->WindowVolumeChanged(); |
michael@0 | 849 | } |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | struct CountWindowData |
michael@0 | 853 | { |
michael@0 | 854 | CountWindowData(nsIDOMWindow* aWindow) |
michael@0 | 855 | : mWindow(aWindow) |
michael@0 | 856 | , mCount(0) |
michael@0 | 857 | {} |
michael@0 | 858 | |
michael@0 | 859 | nsIDOMWindow* mWindow; |
michael@0 | 860 | uint32_t mCount; |
michael@0 | 861 | }; |
michael@0 | 862 | |
michael@0 | 863 | PLDHashOperator |
michael@0 | 864 | AudioChannelService::CountWindowEnumerator(AudioChannelAgent* aAgent, |
michael@0 | 865 | AudioChannelAgentData* aUnused, |
michael@0 | 866 | void* aPtr) |
michael@0 | 867 | { |
michael@0 | 868 | CountWindowData* data = static_cast<CountWindowData*>(aPtr); |
michael@0 | 869 | MOZ_ASSERT(aAgent); |
michael@0 | 870 | |
michael@0 | 871 | if (aAgent->Window() == data->mWindow) { |
michael@0 | 872 | ++data->mCount; |
michael@0 | 873 | } |
michael@0 | 874 | |
michael@0 | 875 | return PL_DHASH_NEXT; |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | uint32_t |
michael@0 | 879 | AudioChannelService::CountWindow(nsIDOMWindow* aWindow) |
michael@0 | 880 | { |
michael@0 | 881 | CountWindowData data(aWindow); |
michael@0 | 882 | mAgents.EnumerateRead(CountWindowEnumerator, &data); |
michael@0 | 883 | return data.mCount; |
michael@0 | 884 | } |
michael@0 | 885 | |
michael@0 | 886 | /* static */ const nsAttrValue::EnumTable* |
michael@0 | 887 | AudioChannelService::GetAudioChannelTable() |
michael@0 | 888 | { |
michael@0 | 889 | return kMozAudioChannelAttributeTable; |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | /* static */ AudioChannel |
michael@0 | 893 | AudioChannelService::GetAudioChannel(const nsAString& aChannel) |
michael@0 | 894 | { |
michael@0 | 895 | for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) { |
michael@0 | 896 | if (aChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) { |
michael@0 | 897 | return static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value); |
michael@0 | 898 | } |
michael@0 | 899 | } |
michael@0 | 900 | |
michael@0 | 901 | return AudioChannel::Normal; |
michael@0 | 902 | } |
michael@0 | 903 | |
michael@0 | 904 | /* static */ AudioChannel |
michael@0 | 905 | AudioChannelService::GetDefaultAudioChannel() |
michael@0 | 906 | { |
michael@0 | 907 | nsString audioChannel = Preferences::GetString("media.defaultAudioChannel"); |
michael@0 | 908 | if (audioChannel.IsEmpty()) { |
michael@0 | 909 | return AudioChannel::Normal; |
michael@0 | 910 | } |
michael@0 | 911 | |
michael@0 | 912 | for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) { |
michael@0 | 913 | if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) { |
michael@0 | 914 | return static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value); |
michael@0 | 915 | } |
michael@0 | 916 | } |
michael@0 | 917 | |
michael@0 | 918 | return AudioChannel::Normal; |
michael@0 | 919 | } |
michael@0 | 920 | |
michael@0 | 921 | /* static */ void |
michael@0 | 922 | AudioChannelService::GetAudioChannelString(AudioChannel aChannel, |
michael@0 | 923 | nsAString& aString) |
michael@0 | 924 | { |
michael@0 | 925 | aString.AssignASCII("normal"); |
michael@0 | 926 | |
michael@0 | 927 | for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) { |
michael@0 | 928 | if (aChannel == |
michael@0 | 929 | static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value)) { |
michael@0 | 930 | aString.AssignASCII(kMozAudioChannelAttributeTable[i].tag); |
michael@0 | 931 | break; |
michael@0 | 932 | } |
michael@0 | 933 | } |
michael@0 | 934 | } |
michael@0 | 935 | |
michael@0 | 936 | /* static */ void |
michael@0 | 937 | AudioChannelService::GetDefaultAudioChannelString(nsAString& aString) |
michael@0 | 938 | { |
michael@0 | 939 | aString.AssignASCII("normal"); |
michael@0 | 940 | |
michael@0 | 941 | nsString audioChannel = Preferences::GetString("media.defaultAudioChannel"); |
michael@0 | 942 | if (!audioChannel.IsEmpty()) { |
michael@0 | 943 | for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) { |
michael@0 | 944 | if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) { |
michael@0 | 945 | aString = audioChannel; |
michael@0 | 946 | break; |
michael@0 | 947 | } |
michael@0 | 948 | } |
michael@0 | 949 | } |
michael@0 | 950 | } |