Bug 1685822 [wpt PR 27117] - [Import Maps] Add tests for rejecting multiple import...
[gecko.git] / dom / media / AudioNotificationSender.cpp
blobfd1cd058e517e156a733f02261042bf8e9b86a7c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AudioNotificationSender.h"
8 #include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
9 #include "mozilla/dom/ContentParent.h" // for ContentParent
10 #include "mozilla/Logging.h" // for LazyLogModule
11 #include "mozilla/RefPtr.h" // for RefPtr
12 #include "mozilla/StaticPtr.h" // for StaticAutoPtr
13 #include "nsAppRunner.h" // for XRE_IsParentProcess
14 #include "nsTArray.h" // for nsTArray
15 #include <mmdeviceapi.h> // for IMMNotificationClient interface
17 static mozilla::LazyLogModule sLogger("AudioNotificationSender");
19 #undef ANS_LOG
20 #define ANS_LOG(...) MOZ_LOG(sLogger, mozilla::LogLevel::Debug, (__VA_ARGS__))
21 #undef ANS_LOGW
22 #define ANS_LOGW(...) \
23 MOZ_LOG(sLogger, mozilla::LogLevel::Warning, (__VA_ARGS__))
25 namespace mozilla {
26 namespace audio {
29 * A runnable task to notify the audio device-changed event.
31 class AudioDeviceChangedRunnable final : public Runnable {
32 public:
33 explicit AudioDeviceChangedRunnable()
34 : Runnable("AudioDeviceChangedRunnable") {}
36 NS_IMETHOD Run() override {
37 MOZ_ASSERT(NS_IsMainThread());
39 nsTArray<dom::ContentParent*> parents;
40 dom::ContentParent::GetAll(parents);
41 for (dom::ContentParent* p : parents) {
42 Unused << p->SendAudioDefaultDeviceChange();
44 return NS_OK;
46 }; // class AudioDeviceChangedRunnable
49 * An observer for receiving audio device events from Windows.
51 typedef void (*DefaultDeviceChangedCallback)();
52 class AudioNotification final : public IMMNotificationClient {
53 public:
54 explicit AudioNotification(DefaultDeviceChangedCallback aCallback)
55 : mCallback(aCallback), mRefCt(0), mIsRegistered(false) {
56 MOZ_COUNT_CTOR(AudioNotification);
57 MOZ_ASSERT(mCallback);
58 const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
59 const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
60 HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr,
61 CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator,
62 getter_AddRefs(mDeviceEnumerator));
64 if (FAILED(hr)) {
65 ANS_LOGW("Cannot create an IMMDeviceEnumerator instance.");
66 return;
69 hr = mDeviceEnumerator->RegisterEndpointNotificationCallback(this);
70 if (FAILED(hr)) {
71 ANS_LOGW("Cannot register notification callback.");
72 return;
75 ANS_LOG("Register notification callback successfully.");
76 mIsRegistered = true;
79 ~AudioNotification() {
80 MOZ_COUNT_DTOR(AudioNotification);
81 // Assert mIsRegistered is true when we have mDeviceEnumerator.
82 // Don't care mIsRegistered if there is no mDeviceEnumerator.
83 MOZ_ASSERT(!mDeviceEnumerator || mIsRegistered);
84 if (!mDeviceEnumerator) {
85 ANS_LOG("No device enumerator in use.");
86 return;
89 HRESULT hr =
90 mDeviceEnumerator->UnregisterEndpointNotificationCallback(this);
91 if (FAILED(hr)) {
92 // We can't really do anything here, so we just add a log for debugging.
93 ANS_LOGW("Unregister notification failed.");
94 } else {
95 ANS_LOG("Unregister notification callback successfully.");
98 mIsRegistered = false;
101 // True whenever the notification server is set to report events to this
102 // object.
103 bool IsRegistered() const { return mIsRegistered; }
105 // IMMNotificationClient Implementation
106 HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow aFlow, ERole aRole,
107 LPCWSTR aDeviceId) override {
108 ANS_LOG("Default device has changed: flow %d, role: %d\n", aFlow, aRole);
109 mCallback();
110 return S_OK;
113 // The remaining methods are not implemented. they simply log when called
114 // (if log is enabled), for debugging.
115 HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR aDeviceId) override {
116 ANS_LOG("Audio device added.");
117 return S_OK;
120 HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR aDeviceId) override {
121 ANS_LOG("Audio device removed.");
122 return S_OK;
125 HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR aDeviceId,
126 DWORD aNewState) override {
127 ANS_LOG("Audio device state changed.");
128 return S_OK;
131 HRESULT STDMETHODCALLTYPE
132 OnPropertyValueChanged(LPCWSTR aDeviceId, const PROPERTYKEY aKey) override {
133 ANS_LOG("Audio device property value changed.");
134 return S_OK;
137 // IUnknown Implementation
138 ULONG STDMETHODCALLTYPE AddRef() override {
139 return InterlockedIncrement(&mRefCt);
142 ULONG STDMETHODCALLTYPE Release() override {
143 ULONG ulRef = InterlockedDecrement(&mRefCt);
144 if (0 == ulRef) {
145 delete this;
147 return ulRef;
150 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
151 VOID** ppvInterface) override {
152 if (__uuidof(IUnknown) == riid) {
153 AddRef();
154 *ppvInterface = static_cast<IUnknown*>(this);
155 } else if (__uuidof(IMMNotificationClient) == riid) {
156 AddRef();
157 *ppvInterface = static_cast<IMMNotificationClient*>(this);
158 } else {
159 *ppvInterface = NULL;
160 return E_NOINTERFACE;
162 return S_OK;
165 private:
166 RefPtr<IMMDeviceEnumerator> mDeviceEnumerator;
167 DefaultDeviceChangedCallback mCallback;
168 LONG mRefCt;
169 bool mIsRegistered;
170 }; // class AudioNotification
173 * A singleton observer for audio device changed events.
175 static StaticAutoPtr<AudioNotification> sAudioNotification;
178 * AudioNotificationSender Implementation
180 /* static */
181 nsresult AudioNotificationSender::Init() {
182 MOZ_ASSERT(XRE_IsParentProcess());
183 MOZ_ASSERT(NS_IsMainThread());
185 if (!sAudioNotification) {
186 sAudioNotification = new AudioNotification(NotifyDefaultDeviceChanged);
187 ClearOnShutdown(&sAudioNotification);
189 if (!sAudioNotification->IsRegistered()) {
190 ANS_LOGW("The notification sender cannot be initialized.");
191 return NS_ERROR_FAILURE;
193 ANS_LOG("The notification sender is initailized successfully.");
196 return NS_OK;
199 /* static */
200 void AudioNotificationSender::NotifyDefaultDeviceChanged() {
201 // This is running on the callback thread (from OnDefaultDeviceChanged).
202 MOZ_ASSERT(XRE_IsParentProcess());
203 ANS_LOG("Notify the default device-changed event.");
205 RefPtr<AudioDeviceChangedRunnable> runnable =
206 new AudioDeviceChangedRunnable();
207 NS_DispatchToMainThread(runnable);
210 } // namespace audio
211 } // namespace mozilla