Bug 1449388 - Send NPNVaudioDeviceStateChanged to plugins when any Windows audio device changes state. r=jimm a=lizzard
authorDavid Parks <dparks@mozilla.com>
Wed, 13 Jun 2018 13:12:54 -0700
changeset 477847 5f79cf2a5110c661dd49bddf6dccdc9cccea9050
parent 477846 83d54bf1905f4c71190b5438eeedb9fef3c55c4b
child 477848 6ab86ccd136dbaa90824c81ce76469ba83b80c71
push id9445
push userarchaeopteryx@coole-files.de
push dateSat, 07 Jul 2018 21:43:02 +0000
treeherdermozilla-beta@0a939a1cd7f9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm, lizzard
bugs1449388
milestone62.0
Bug 1449388 - Send NPNVaudioDeviceStateChanged to plugins when any Windows audio device changes state. r=jimm a=lizzard This message allows plugins to detect when any audio devices change state, even when running inside of our plugin sandbox.
dom/plugins/base/npapi.h
dom/plugins/ipc/PPluginModule.ipdl
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginMessageUtils.h
dom/plugins/ipc/PluginModuleChild.cpp
dom/plugins/ipc/PluginModuleChild.h
dom/plugins/ipc/PluginUtilsWin.cpp
--- a/dom/plugins/base/npapi.h
+++ b/dom/plugins/base/npapi.h
@@ -274,16 +274,28 @@ typedef struct _NPAudioDeviceChangeDetai
   int32_t flow;
   int32_t role;
   const wchar_t* defaultDevice;  // this pointer is only valid during the call
                                  // to NPPSetValue.
 } NPAudioDeviceChangeDetails;
 
 #endif /* XP_WIN */
 
+/*
+ * This is the value of the NPNVaudioDeviceStateChanged variable.
+ */
+typedef struct _NPAudioDeviceStateChanged
+{
+  /* Name of device that changed state.  This string is only valid during
+   * the call to NPPSetValue.
+   */
+  const wchar_t* device;
+  uint32_t newState;
+} NPAudioDeviceStateChanged;
+
 typedef enum {
   NPDrawingModelDUMMY
 #if defined(XP_MACOSX)
 #ifndef NP_NO_QUICKDRAW
   , NPDrawingModelQuickDraw = 0
 #endif
   , NPDrawingModelCoreGraphics = 1
   , NPDrawingModelOpenGL = 2
@@ -463,16 +475,17 @@ typedef enum {
 #endif
   , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */
   , NPNVsupportsUpdatedCocoaTextInputBool = 3002 /* TRUE if the browser supports the updated
                                                     Cocoa text input specification. */
 #endif
   , NPNVmuteAudioBool = 4000 /* Request that the browser wants to mute or unmute the plugin */
 #if defined(XP_WIN)
   , NPNVaudioDeviceChangeDetails = 4001 /* Provides information about the new default audio device */
+  , NPNVaudioDeviceStateChanged = 4002 /* Provides information if any audio device changes state */
 #endif
 #if defined(XP_MACOSX)
   , NPNVsupportsCompositingCoreAnimationPluginsBool = 74656 /* TRUE if the browser supports
                                                                CA model compositing */
 #endif
   , NPNVLast
 } NPNVariable;
 
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -10,16 +10,17 @@ include protocol PProfiler;
 include protocol PFunctionBroker;
 
 using NPError from "npapi.h";
 using NPNVariable from "npapi.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
 using struct nsID from "nsID.h";
 using struct mozilla::plugins::NPAudioDeviceChangeDetailsIPC from "mozilla/plugins/PluginMessageUtils.h";
+using struct mozilla::plugins::NPAudioDeviceStateChangedIPC from "mozilla/plugins/PluginMessageUtils.h";
 
 namespace mozilla {
 namespace plugins {
 
 struct PluginSettings
 {
   // These settings correspond to NPNVariable. They are fetched from
   // mozilla::plugins::parent::_getvalue.
@@ -88,16 +89,17 @@ child:
   async SetParentHangTimeout(uint32_t seconds);
 
   intr InitCrashReporter(Shmem shmem)
     returns (NativeThreadId tid);
 
   async SettingChanged(PluginSettings settings);
 
   async NPP_SetValue_NPNVaudioDeviceChangeDetails(NPAudioDeviceChangeDetailsIPC changeDetails);
+  async NPP_SetValue_NPNVaudioDeviceStateChanged(NPAudioDeviceStateChangedIPC deviceState);
 
   async InitPluginModuleChild(Endpoint<PPluginModuleChild> endpoint);
 
   async InitPluginFunctionBroker(Endpoint<PFunctionBrokerChild> endpoint);
 
 parent:
   /**
    * This message is only used on X11 platforms.
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -805,19 +805,27 @@ PluginInstanceChild::AnswerNPP_SetValue_
 NPError
 PluginInstanceChild::DefaultAudioDeviceChanged(NPAudioDeviceChangeDetails& details)
 {
     if (!mPluginIface->setvalue) {
         return NPERR_GENERIC_ERROR;
     }
     return mPluginIface->setvalue(GetNPP(), NPNVaudioDeviceChangeDetails, (void*)&details);
 }
+
+NPError
+PluginInstanceChild::AudioDeviceStateChanged(NPAudioDeviceStateChanged& aDeviceState)
+{
+  if (!mPluginIface->setvalue) {
+    return NPERR_GENERIC_ERROR;
+  }
+  return mPluginIface->setvalue(GetNPP(), NPNVaudioDeviceStateChanged, (void*)&aDeviceState);
+}
 #endif
 
-
 mozilla::ipc::IPCResult
 PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
                                            int16_t* handled)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
     AutoStackHelper guard(this);
 
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -251,16 +251,17 @@ public:
     void DoAsyncRedraw();
 
     virtual mozilla::ipc::IPCResult RecvHandledWindowedPluginKeyEvent(
         const NativeEventData& aKeyEventData,
         const bool& aIsConsumed) override;
 
 #if defined(XP_WIN)
     NPError DefaultAudioDeviceChanged(NPAudioDeviceChangeDetails& details);
+    NPError AudioDeviceStateChanged(NPAudioDeviceStateChanged& aDeviceState);
 #endif
 
 private:
     friend class PluginModuleChild;
 
     NPError
     InternalGetNPObjectForValue(NPNVariable aValue,
                                 NPObject** aObject);
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -105,16 +105,22 @@ struct NPRemoteWindow
 // requiring C++ objects in npapi.h.
 struct NPAudioDeviceChangeDetailsIPC
 {
   int32_t flow;
   int32_t role;
   std::wstring defaultDevice;
 };
 
+struct NPAudioDeviceStateChangedIPC
+{
+  std::wstring device;
+  uint32_t state;
+};
+
 #ifdef XP_WIN
 typedef HWND NativeWindowHandle;
 #elif defined(MOZ_X11)
 typedef XID NativeWindowHandle;
 #elif defined(XP_DARWIN) || defined(ANDROID)
 typedef intptr_t NativeWindowHandle; // never actually used, will always be 0
 #else
 #error Need NativeWindowHandle for this platform
@@ -647,16 +653,45 @@ struct ParamTraits<mozilla::plugins::NPA
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     aLog->append(StringPrintf(L"[%d, %d, %S]", aParam.flow, aParam.role,
                               aParam.defaultDevice.c_str()));
   }
 };
 
+
+template <>
+struct ParamTraits<mozilla::plugins::NPAudioDeviceStateChangedIPC>
+{
+  typedef mozilla::plugins::NPAudioDeviceStateChangedIPC paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.device);
+    WriteParam(aMsg, aParam.state);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    int32_t state;
+    std::wstring device;
+    if (ReadParam(aMsg, aIter, &device) && ReadParam(aMsg, aIter, &state)) {
+      aResult->device = device;
+      aResult->state = state;
+      return true;
+    }
+    return false;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+    aLog->append(StringPrintf(L"[%S,%d]", aParam.device.c_str(), aParam.state));
+  }
+};
 } /* namespace IPC */
 
 
 // Serializing NPEvents is completely platform-specific and can be rather
 // intricate depending on the platform.  So for readability we split it
 // into separate files and have the only macro crud live here.
 //
 // NB: these guards are based on those where struct NPEvent is defined
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -2232,8 +2232,26 @@ PluginModuleChild::RecvNPP_SetValue_NPNV
       PluginInstanceChild* pluginInst = iter.Get()->GetKey();
       pluginInst->DefaultAudioDeviceChanged(details);
     }
     return IPC_OK();
 #else
     MOZ_CRASH("NPP_SetValue_NPNVaudioDeviceChangeDetails is a Windows-only message");
 #endif
 }
+
+mozilla::ipc::IPCResult
+PluginModuleChild::RecvNPP_SetValue_NPNVaudioDeviceStateChanged(
+                          const NPAudioDeviceStateChangedIPC& aDeviceStateIPC)
+{
+#if defined(XP_WIN)
+  NPAudioDeviceStateChanged stateChange;
+  stateChange.newState = aDeviceStateIPC.state;
+  stateChange.device = aDeviceStateIPC.device.c_str();
+  for (auto iter = mAudioNotificationSet.ConstIter(); !iter.Done(); iter.Next()) {
+    PluginInstanceChild* pluginInst = iter.Get()->GetKey();
+    pluginInst->AudioDeviceStateChanged(stateChange);
+  }
+  return IPC_OK();
+#else
+  MOZ_CRASH("NPP_SetValue_NPNVaudioDeviceRemoved is a Windows-only message");
+#endif
+}
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -206,16 +206,18 @@ public:
     int GetQuirks() { return mQuirks; }
 
     const PluginSettings& Settings() const { return mCachedSettings; }
 
     NPError PluginRequiresAudioDeviceChanges(PluginInstanceChild* aInstance,
                                              NPBool aShouldRegister);
     mozilla::ipc::IPCResult RecvNPP_SetValue_NPNVaudioDeviceChangeDetails(
         const NPAudioDeviceChangeDetailsIPC& detailsIPC) override;
+    mozilla::ipc::IPCResult RecvNPP_SetValue_NPNVaudioDeviceStateChanged(
+      const NPAudioDeviceStateChangedIPC& aDeviceStateIPC) override;
 
 private:
     NPError DoNP_Initialize(const PluginSettings& aSettings);
     void AddQuirk(PluginQuirks quirk) {
       if (mQuirks == QUIRKS_NOT_INITIALIZED)
         mQuirks = 0;
       mQuirks |= quirk;
     }
--- a/dom/plugins/ipc/PluginUtilsWin.cpp
+++ b/dom/plugins/ipc/PluginUtilsWin.cpp
@@ -12,43 +12,67 @@
 
 namespace mozilla {
 namespace plugins {
 namespace PluginUtilsWin {
 
 typedef nsTHashtable<nsPtrHashKey<PluginModuleParent>> PluginModuleSet;
 StaticMutex sMutex;
 
-class AudioDeviceChangedRunnable : public Runnable
+class AudioDeviceMessageRunnable : public Runnable
 {
 public:
-  explicit AudioDeviceChangedRunnable(const PluginModuleSet* aAudioNotificationSet,
+  explicit AudioDeviceMessageRunnable(const PluginModuleSet* aAudioNotificationSet,
                                       NPAudioDeviceChangeDetailsIPC aChangeDetails) :
-    Runnable("AudioDeviceChangedRunnable")
+    Runnable("AudioDeviceMessageRunnableCD")
     , mChangeDetails(aChangeDetails)
+    , mMessageType(DEFAULT_DEVICE_CHANGED)
+    , mAudioNotificationSet(aAudioNotificationSet)
+  {}
+
+  explicit AudioDeviceMessageRunnable(const PluginModuleSet* aAudioNotificationSet,
+                                      NPAudioDeviceStateChangedIPC aDeviceState) :
+    Runnable("AudioDeviceMessageRunnableSC")
+    , mDeviceState(aDeviceState)
+    , mMessageType(DEVICE_STATE_CHANGED)
     , 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();
-      if(!pluginModule->SendNPP_SetValue_NPNVaudioDeviceChangeDetails(mChangeDetails)) {
+      bool success = false;
+      switch (mMessageType) {
+      case DEFAULT_DEVICE_CHANGED:
+        success = pluginModule->SendNPP_SetValue_NPNVaudioDeviceChangeDetails(mChangeDetails);
+        break;
+      case DEVICE_STATE_CHANGED:
+        success = pluginModule->SendNPP_SetValue_NPNVaudioDeviceStateChanged(mDeviceState);
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("bad AudioDeviceMessageRunnable state");
+      }
+      if(!success) {
         return NS_ERROR_FAILURE;
       }
     }
     return NS_OK;
   }
 
 protected:
+  // The potential payloads for the message.  Type determined by mMessageType.
   NPAudioDeviceChangeDetailsIPC mChangeDetails;
+  NPAudioDeviceStateChangedIPC mDeviceState;
+  enum { DEFAULT_DEVICE_CHANGED, DEVICE_STATE_CHANGED } mMessageType;
+
   const PluginModuleSet* mAudioNotificationSet;
 };
 
 class AudioNotification final : public IMMNotificationClient
 {
 public:
   AudioNotification() :
       mIsRegistered(false)
@@ -77,18 +101,18 @@ public:
   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);
+    RefPtr<AudioDeviceMessageRunnable> runnable =
+      new AudioDeviceMessageRunnable(&mAudioNotificationSet, changeDetails);
     NS_DispatchToMainThread(runnable);
     return S_OK;
   }
 
   HRESULT STDMETHODCALLTYPE
   OnDeviceAdded(LPCWSTR device_id) override
   {
     return S_OK;
@@ -98,16 +122,24 @@ public:
   OnDeviceRemoved(LPCWSTR device_id) override
   {
     return S_OK;
   }
 
   HRESULT STDMETHODCALLTYPE
   OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) override
   {
+    NPAudioDeviceStateChangedIPC deviceStateIPC;
+    deviceStateIPC.device = device_id ? std::wstring(device_id) : L"";
+    deviceStateIPC.state = (uint32_t)new_state;
+
+    // Make sure that plugin is notified on the main thread.
+    RefPtr<AudioDeviceMessageRunnable> runnable =
+      new AudioDeviceMessageRunnable(&mAudioNotificationSet, deviceStateIPC);
+    NS_DispatchToMainThread(runnable);
     return S_OK;
   }
 
   HRESULT STDMETHODCALLTYPE
   OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) override
   {
     return S_OK;
   }