Bug 1041599 - Maintain a single volume control session between browser, content, and plugins on Windows. r=aklotz

MozReview-Commit-ID: 2LuYciKfsWn
This commit is contained in:
Jim Mathies 2016-03-13 08:25:23 -05:00
parent 9f5f38e8d7
commit ee3a95d698
8 changed files with 92 additions and 32 deletions

View File

@ -147,6 +147,7 @@
#ifdef XP_WIN
#include <process.h>
#define getpid _getpid
#include "mozilla/widget/AudioSession.h"
#endif
#ifdef MOZ_X11
@ -3043,6 +3044,10 @@ ContentChild::RecvShutdown()
"content-child-shutdown", nullptr);
}
#if defined(XP_WIN)
mozilla::widget::StopAudioSession();
#endif
GetIPCChannel()->SetAbortOnError(false);
#ifdef MOZ_ENABLE_PROFILER_SPS
@ -3161,6 +3166,26 @@ ContentChild::RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent)
return true;
}
bool
ContentChild::RecvSetAudioSessionData(const nsID& aId,
const nsString& aDisplayName,
const nsString& aIconPath)
{
#if defined(XP_WIN)
if (NS_FAILED(mozilla::widget::RecvAudioSessionData(aId, aDisplayName,
aIconPath))) {
return true;
}
// Ignore failures here; we can't really do anything about them
mozilla::widget::StartAudioSession();
return true;
#else
NS_RUNTIMEABORT("Not Reached!");
return false;
#endif
}
// This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp
// can't include ContentChild.h since it includes windows.h.

View File

@ -610,6 +610,12 @@ public:
virtual bool RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent) override;
// Windows specific - set up audio session
virtual bool
RecvSetAudioSessionData(const nsID& aId,
const nsString& aDisplayName,
const nsString& aIconPath) override;
private:
virtual void ActorDestroy(ActorDestroyReason why) override;

View File

@ -263,6 +263,10 @@ using namespace mozilla::system;
#include "mozilla/dom/GamepadMonitoring.h"
#endif
#ifdef XP_WIN
#include "mozilla/widget/AudioSession.h"
#endif
#include "VRManagerParent.h" // for VRManagerParent
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
@ -2620,6 +2624,16 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
KillHard("SandboxInitFailed");
}
#endif
#if defined(XP_WIN)
// Send the info needed to join the browser process's audio session.
nsID id;
nsString sessionName;
nsString iconPath;
if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
iconPath))) {
Unused << SendSetAudioSessionData(id, sessionName, iconPath);
}
#endif
}
bool

View File

@ -742,6 +742,13 @@ child:
*/
async PushSubscriptionChange(nsCString scope, Principal principal);
/**
* Windows specific: associate this content process with the browsers
* audio session.
*/
async SetAudioSessionData(nsID aID,
nsString aDisplayName,
nsString aIconPath);
parent:
/**
* Tell the content process some attributes of itself. This is

View File

@ -2365,8 +2365,8 @@ PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
SetPluginFuncs(mNPPIface);
}
// Send the info needed to join the chrome process's audio session to the
// plugin process
// Send the info needed to join the browser process's audio session to the
// plugin process.
nsID id;
nsString sessionName;
nsString iconPath;

View File

@ -104,6 +104,7 @@
#include <math.h>
#include "cairo/cairo-features.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/widget/AudioSession.h"
#ifndef PROCESS_DEP_ENABLE
#define PROCESS_DEP_ENABLE 0x1
@ -4420,6 +4421,10 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
mScopedXPCOM = nullptr;
#if defined(XP_WIN)
mozilla::widget::StopAudioSession();
#endif
// unlock the profile after ScopedXPCOMStartup object (xpcom)
// has gone out of scope. see bug #386739 for more details
mProfileLock->Unlock();

View File

@ -76,7 +76,7 @@ public:
UNINITIALIZED, // Has not been initialized yet
STARTED, // Started
CLONED, // SetSessionInfoCalled, Start not called
FAILED, // The autdio session failed to start
FAILED, // The audio session failed to start
STOPPED, // Stop called
AUDIO_SESSION_DISCONNECTED // Audio session disconnected
};
@ -185,16 +185,18 @@ AudioSession::Start()
HRESULT hr;
// Don't check for errors in case something already initialized COM
// on this thread.
CoInitialize(nullptr);
// There's a matching CoUninit in Stop() for this tied to a state of
// UNINITIALIZED.
hr = CoInitialize(nullptr);
MOZ_ASSERT(SUCCEEDED(hr), "CoInitialize failure in audio session control, unexpected");
if (mState == UNINITIALIZED) {
mState = FAILED;
// XXXkhuey implement this for content processes
if (XRE_IsContentProcess())
// Content processes should be CLONED
if (XRE_IsContentProcess()) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(XRE_IsParentProcess(),
"Should only get here in a chrome process!");
@ -212,9 +214,6 @@ AudioSession::Start()
wchar_t *buffer;
mIconPath.GetMutableData(&buffer, MAX_PATH);
// XXXkhuey we should provide a way for a xulrunner app to specify an icon
// that's not in the product binary.
::GetModuleFileNameW(nullptr, buffer, MAX_PATH);
nsCOMPtr<nsIUUIDGenerator> uuidgen =
@ -252,16 +251,19 @@ AudioSession::Start()
CLSCTX_ALL,
nullptr,
getter_AddRefs(manager));
if (FAILED(hr))
if (FAILED(hr)) {
return NS_ERROR_FAILURE;
}
hr = manager->GetAudioSessionControl(nullptr,
FALSE,
hr = manager->GetAudioSessionControl(&GUID_NULL,
0,
getter_AddRefs(mAudioSessionControl));
if (FAILED(hr))
return NS_ERROR_FAILURE;
hr = mAudioSessionControl->SetGroupingParam((LPCGUID)&mSessionGroupingParameter,
if (FAILED(hr)) {
return NS_ERROR_FAILURE;
}
hr = mAudioSessionControl->SetGroupingParam((LPGUID)&mSessionGroupingParameter,
nullptr);
if (FAILED(hr)) {
StopInternal();
@ -294,10 +296,7 @@ AudioSession::Start()
void
AudioSession::StopInternal()
{
static const nsID blankId = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0} };
if (mAudioSessionControl) {
mAudioSessionControl->SetGroupingParam((LPCGUID)&blankId, nullptr);
mAudioSessionControl->UnregisterAudioSessionNotification(this);
mAudioSessionControl = nullptr;
}
@ -310,18 +309,19 @@ AudioSession::Stop()
mState == UNINITIALIZED || // XXXremove this
mState == FAILED,
"State invariants violated");
SessionState state = mState;
mState = STOPPED;
RefPtr<AudioSession> kungFuDeathGrip;
kungFuDeathGrip.swap(sService);
{
RefPtr<AudioSession> kungFuDeathGrip;
kungFuDeathGrip.swap(sService);
if (!XRE_IsContentProcess())
StopInternal();
}
// At this point kungFuDeathGrip should be the only reference to AudioSession
::CoUninitialize();
if (state != UNINITIALIZED) {
::CoUninitialize();
}
return NS_OK;
}

View File

@ -246,9 +246,14 @@ nsAppShell::Init()
NS_IMETHODIMP
nsAppShell::Run(void)
{
// Ignore failure; failing to start the application is not exactly an
// appropriate response to failing to start an audio session.
mozilla::widget::StartAudioSession();
// Content processes initialize audio later through PContent using audio
// tray id information pulled from the browser process AudioSession. This
// way the two share a single volume control.
// Note StopAudioSession() is called from nsAppRunner.cpp after xpcom is torn
// down to insure the browser shuts down after child processes.
if (XRE_IsParentProcess()) {
mozilla::widget::StartAudioSession();
}
// Add an observer that disables the screen saver when requested by Gecko.
// For example when we're playing video in the foreground tab.
@ -258,8 +263,6 @@ nsAppShell::Run(void)
RemoveScreenWakeLockListener();
mozilla::widget::StopAudioSession();
return rv;
}