summaryrefslogtreecommitdiff
path: root/dom/plugins/ipc/PluginUtilsWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/ipc/PluginUtilsWin.cpp')
-rw-r--r--dom/plugins/ipc/PluginUtilsWin.cpp237
1 files changed, 237 insertions, 0 deletions
diff --git a/dom/plugins/ipc/PluginUtilsWin.cpp b/dom/plugins/ipc/PluginUtilsWin.cpp
new file mode 100644
index 000000000..db6387a51
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsWin.cpp
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* PluginUtilsWin.cpp - top-level Windows plugin management code */
+
+#include <mmdeviceapi.h>
+#include "PluginUtilsWin.h"
+#include "PluginModuleParent.h"
+#include "mozilla/StaticMutex.h"
+
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsWin {
+
+typedef nsTHashtable<nsPtrHashKey<PluginModuleParent>> PluginModuleSet;
+StaticMutex sMutex;
+
+class AudioDeviceChangedRunnable : public Runnable
+{
+public:
+ explicit AudioDeviceChangedRunnable(const PluginModuleSet* aAudioNotificationSet,
+ NPAudioDeviceChangeDetailsIPC aChangeDetails) :
+ mChangeDetails(aChangeDetails)
+ , mAudioNotificationSet(aAudioNotificationSet)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ StaticMutexAutoLock lock(sMutex);
+ PLUGIN_LOG_DEBUG(("Notifying %d plugins of audio device change.",
+ mAudioNotificationSet->Count()));
+
+ for (auto iter = mAudioNotificationSet->ConstIter(); !iter.Done(); iter.Next()) {
+ PluginModuleParent* pluginModule = iter.Get()->GetKey();
+ pluginModule->SendNPP_SetValue_NPNVaudioDeviceChangeDetails(mChangeDetails);
+ }
+ return NS_OK;
+ }
+
+protected:
+ NPAudioDeviceChangeDetailsIPC mChangeDetails;
+ const PluginModuleSet* mAudioNotificationSet;
+};
+
+class AudioNotification : public IMMNotificationClient
+{
+public:
+ AudioNotification() :
+ mRefCt(1)
+ , mIsRegistered(false)
+ {
+ HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&mDeviceEnum));
+ if (FAILED(hr)) {
+ mDeviceEnum = nullptr;
+ return;
+ }
+
+ hr = mDeviceEnum->RegisterEndpointNotificationCallback(this);
+ if (FAILED(hr)) {
+ mDeviceEnum->Release();
+ mDeviceEnum = nullptr;
+ return;
+ }
+
+ mIsRegistered = true;
+ }
+
+ ~AudioNotification()
+ {
+ MOZ_ASSERT(!mIsRegistered,
+ "Destroying AudioNotification without first calling Unregister");
+ if (mDeviceEnum) {
+ mDeviceEnum->Release();
+ }
+ }
+
+ // IMMNotificationClient Implementation
+ HRESULT STDMETHODCALLTYPE
+ OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id) override
+ {
+ NPAudioDeviceChangeDetailsIPC changeDetails;
+ changeDetails.flow = (int32_t)flow;
+ changeDetails.role = (int32_t)role;
+ changeDetails.defaultDevice = device_id ? std::wstring(device_id) : L"";
+
+ // Make sure that plugin is notified on the main thread.
+ RefPtr<AudioDeviceChangedRunnable> runnable =
+ new AudioDeviceChangedRunnable(&mAudioNotificationSet, changeDetails);
+ NS_DispatchToMainThread(runnable);
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ OnDeviceAdded(LPCWSTR device_id) override
+ {
+ return S_OK;
+ };
+
+ HRESULT STDMETHODCALLTYPE
+ OnDeviceRemoved(LPCWSTR device_id) override
+ {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) override
+ {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) override
+ {
+ return S_OK;
+ }
+
+ // IUnknown Implementation
+ ULONG STDMETHODCALLTYPE
+ AddRef() override
+ {
+ return InterlockedIncrement(&mRefCt);
+ }
+
+ ULONG STDMETHODCALLTYPE
+ Release() override
+ {
+ ULONG ulRef = InterlockedDecrement(&mRefCt);
+ if (0 == ulRef) {
+ delete this;
+ }
+ return ulRef;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ QueryInterface(REFIID riid, VOID **ppvInterface) override
+ {
+ if (__uuidof(IUnknown) == riid) {
+ AddRef();
+ *ppvInterface = (IUnknown*)this;
+ } else if (__uuidof(IMMNotificationClient) == riid) {
+ AddRef();
+ *ppvInterface = (IMMNotificationClient*)this;
+ } else {
+ *ppvInterface = NULL;
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+ }
+
+ /*
+ * A Valid instance must be Unregistered before Releasing it.
+ */
+ void Unregister()
+ {
+ if (mDeviceEnum) {
+ mDeviceEnum->UnregisterEndpointNotificationCallback(this);
+ }
+ mIsRegistered = false;
+ }
+
+ /*
+ * True whenever the notification server is set to report events to this object.
+ */
+ bool IsRegistered() {
+ return mIsRegistered;
+ }
+
+ void AddModule(PluginModuleParent* aModule) {
+ StaticMutexAutoLock lock(sMutex);
+ mAudioNotificationSet.PutEntry(aModule);
+ }
+
+ void RemoveModule(PluginModuleParent* aModule) {
+ StaticMutexAutoLock lock(sMutex);
+ mAudioNotificationSet.RemoveEntry(aModule);
+ }
+
+ /*
+ * Are any modules registered for audio notifications?
+ */
+ bool HasModules() {
+ return !mAudioNotificationSet.IsEmpty();
+ }
+
+private:
+ bool mIsRegistered; // only used to make sure that Unregister is called before destroying a Valid instance.
+ LONG mRefCt;
+ IMMDeviceEnumerator* mDeviceEnum;
+
+ // Set of plugin modules that have registered to be notified when the audio device
+ // changes.
+ PluginModuleSet mAudioNotificationSet;
+}; // class AudioNotification
+
+// callback that gets notified of audio device events, or NULL
+AudioNotification* sAudioNotification = nullptr;
+
+nsresult
+RegisterForAudioDeviceChanges(PluginModuleParent* aModuleParent, bool aShouldRegister)
+{
+ // Hold the AudioNotification singleton iff there are PluginModuleParents
+ // that are subscribed to it.
+ if (aShouldRegister) {
+ if (!sAudioNotification) {
+ // We are registering the first module. Create the singleton.
+ sAudioNotification = new AudioNotification();
+ if (!sAudioNotification->IsRegistered()) {
+ PLUGIN_LOG_DEBUG(("Registered for plugin audio device notification failed."));
+ sAudioNotification->Release();
+ sAudioNotification = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+ PLUGIN_LOG_DEBUG(("Registered for plugin audio device notification."));
+ }
+ sAudioNotification->AddModule(aModuleParent);
+ }
+ else if (!aShouldRegister && sAudioNotification) {
+ sAudioNotification->RemoveModule(aModuleParent);
+ if (!sAudioNotification->HasModules()) {
+ // We have removed the last module from the notification mechanism
+ // so we can destroy the singleton.
+ PLUGIN_LOG_DEBUG(("Unregistering for plugin audio device notification."));
+ sAudioNotification->Unregister();
+ sAudioNotification->Release();
+ sAudioNotification = nullptr;
+ }
+ }
+ return NS_OK;
+}
+
+} // namespace PluginUtilsWin
+} // namespace plugins
+} // namespace mozilla