gecko/content/media/gmp/GMPService.cpp
Andrew McCreight 61a5c7e622 Bug 1058903 - Ignore the GMPService shutdown assert, because it always fails. r=jesup
This is failing all the time right now, but we never reach the assert due to crashing earlier.

To allow us to get e10s shutdown leak checking working, ignore this assertion until GMPService is
fixed for e10s.
2014-09-10 14:52:36 -07:00

835 lines
24 KiB
C++

/* -*- 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/. */
#include "GMPService.h"
#include "prlog.h"
#include "GMPParent.h"
#include "GMPVideoDecoderParent.h"
#include "nsIObserverService.h"
#include "GeckoChildProcessHost.h"
#include "mozilla/Preferences.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/SyncRunnable.h"
#include "nsXPCOMPrivate.h"
#include "mozilla/Services.h"
#include "nsNativeCharsetUtils.h"
#include "nsIConsoleService.h"
#include "mozilla/unused.h"
#include "GMPDecryptorParent.h"
#include "GMPAudioDecoderParent.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/Preferences.h"
#include "runnable_utils.h"
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
#include "mozilla/Sandbox.h"
#endif
namespace mozilla {
#ifdef LOG
#undef LOG
#endif
#ifdef PR_LOGGING
PRLogModuleInfo*
GetGMPLog()
{
static PRLogModuleInfo *sLog;
if (!sLog)
sLog = PR_NewLogModule("GMP");
return sLog;
}
#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
#else
#define LOGD(msg)
#define LOG(leve1, msg)
#endif
#ifdef __CLASS__
#undef __CLASS__
#endif
#define __CLASS__ "GMPService"
namespace gmp {
static StaticRefPtr<GeckoMediaPluginService> sSingletonService;
class GMPServiceCreateHelper MOZ_FINAL : public nsRunnable
{
nsRefPtr<GeckoMediaPluginService> mService;
public:
static already_AddRefed<GeckoMediaPluginService>
GetOrCreate()
{
nsRefPtr<GeckoMediaPluginService> service;
if (NS_IsMainThread()) {
service = GetOrCreateOnMainThread();
} else {
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
nsRefPtr<GMPServiceCreateHelper> createHelper = new GMPServiceCreateHelper();
mozilla::SyncRunnable::DispatchToThread(mainThread, createHelper, true);
service = createHelper->mService.forget();
}
return service.forget();
}
private:
GMPServiceCreateHelper()
{
}
~GMPServiceCreateHelper()
{
MOZ_ASSERT(!mService);
}
static already_AddRefed<GeckoMediaPluginService>
GetOrCreateOnMainThread()
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<GeckoMediaPluginService> service = sSingletonService.get();
if (!service) {
service = new GeckoMediaPluginService();
service->Init();
sSingletonService = service;
ClearOnShutdown(&sSingletonService);
}
return service.forget();
}
NS_IMETHOD
Run()
{
MOZ_ASSERT(NS_IsMainThread());
mService = GetOrCreateOnMainThread();
return NS_OK;
}
};
already_AddRefed<GeckoMediaPluginService>
GeckoMediaPluginService::GetGeckoMediaPluginService()
{
return GMPServiceCreateHelper::GetOrCreate();
}
NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver)
#define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000
static int32_t sMaxAsyncShutdownWaitMs = 0;
GeckoMediaPluginService::GeckoMediaPluginService()
: mMutex("GeckoMediaPluginService::mMutex")
, mShuttingDown(false)
, mShuttingDownOnGMPThread(false)
, mWaitingForPluginsAsyncShutdown(false)
{
MOZ_ASSERT(NS_IsMainThread());
static bool setTimeoutPrefCache = false;
if (!setTimeoutPrefCache) {
setTimeoutPrefCache = true;
Preferences::AddIntVarCache(&sMaxAsyncShutdownWaitMs,
"media.gmp.async-shutdown-timeout",
GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT);
}
}
GeckoMediaPluginService::~GeckoMediaPluginService()
{
MOZ_ASSERT(mPlugins.IsEmpty());
MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty());
}
void
GeckoMediaPluginService::Init()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
MOZ_ASSERT(obsService);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
prefs->AddObserver("media.gmp.plugin.crash", this, false);
}
// Kick off scanning for plugins
nsCOMPtr<nsIThread> thread;
unused << GetThread(getter_AddRefs(thread));
}
void
AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure)
{
NS_WARNING("Timed out waiting for GMP async shutdown!");
nsRefPtr<GeckoMediaPluginService> service = sSingletonService.get();
if (service) {
service->AbortAsyncShutdown();
}
}
NS_IMETHODIMP
GeckoMediaPluginService::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aSomeData)
{
LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, aTopic));
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
if (branch) {
bool crashNow = false;
if (NS_LITERAL_STRING("media.gmp.plugin.crash").Equals(aSomeData)) {
branch->GetBoolPref("media.gmp.plugin.crash", &crashNow);
}
if (crashNow) {
nsCOMPtr<nsIThread> gmpThread;
{
MutexAutoLock lock(mMutex);
gmpThread = mGMPThread;
}
if (gmpThread) {
gmpThread->Dispatch(WrapRunnable(this, &GeckoMediaPluginService::CrashPlugins),
NS_DISPATCH_NORMAL);
}
}
}
} else if (!strcmp("profile-change-teardown", aTopic)) {
// How shutdown works:
//
// Some GMPs require time to do bookkeeping upon shutdown. These GMPs
// need to be given time to access storage during shutdown. To signal
// that time to shutdown is required, those GMPs implement the
// GMPAsyncShutdown interface.
//
// When we startup the child process, we query the GMP for the
// GMPAsyncShutdown interface, and if it's present, we send a message
// back to the GMPParent, which then registers the GMPParent by calling
// GMPService::AsyncShutdownNeeded().
//
// On shutdown, we set mWaitingForPluginsAsyncShutdown to true, and then
// call UnloadPlugins on the GMPThread, and process events on the main
// thread until an event sets mWaitingForPluginsAsyncShutdown=false on
// the main thread.
//
// UnloadPlugins() sends close messages for all plugins' API objects to
// the GMP interfaces in the child process, and then sends the async
// shutdown notifications to child GMPs. When a GMP has completed its
// shutdown, it calls GMPAsyncShutdownHost::ShutdownComplete(), which
// sends a message back to the parent, which calls
// GMPService::AsyncShutdownComplete(). If all plugins requiring async
// shutdown have called AsyncShutdownComplete() we stick an event on the
// main thread to set mWaitingForPluginsAsyncShutdown=false. We must use
// an event to do this, as we must ensure the main thread processes an
// event to run its loop. This will unblock the main thread, and shutdown
// of other components will proceed.
//
// We set a timer in UnloadPlugins(), and abort waiting for async
// shutdown if the GMPs are taking too long to shutdown.
//
// We shutdown in "profile-change-teardown", as the profile dir is
// still writable then, and it's required for GMPStorage. We block the
// shutdown process by spinning the main thread event loop until all GMPs
// have shutdown, or timeout has occurred.
//
// GMPStorage needs to work up until the shutdown-complete notification
// arrives from the GMP process.
mWaitingForPluginsAsyncShutdown = true;
nsCOMPtr<nsIThread> gmpThread;
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mShuttingDown);
mShuttingDown = true;
gmpThread = mGMPThread;
}
if (gmpThread) {
gmpThread->Dispatch(
NS_NewRunnableMethod(this, &GeckoMediaPluginService::UnloadPlugins),
NS_DISPATCH_NORMAL);
} else {
MOZ_ASSERT(mPlugins.IsEmpty());
}
// Wait for plugins to do async shutdown...
while (mWaitingForPluginsAsyncShutdown) {
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
}
} else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
nsCOMPtr<nsIThread> gmpThread;
{
MutexAutoLock lock(mMutex);
// XXX The content process never gets profile-change-teardown, so mShuttingDown
// will always be false here. GMPService needs to be proxied to the parent.
// See bug 1057908.
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default || mShuttingDown);
mGMPThread.swap(gmpThread);
}
if (gmpThread) {
gmpThread->Shutdown();
}
}
return NS_OK;
}
// always call with getter_AddRefs, because it does
NS_IMETHODIMP
GeckoMediaPluginService::GetThread(nsIThread** aThread)
{
MOZ_ASSERT(aThread);
// This can be called from any thread.
MutexAutoLock lock(mMutex);
if (!mGMPThread) {
// Don't allow the thread to be created after shutdown has started.
if (mShuttingDown) {
return NS_ERROR_FAILURE;
}
nsresult rv = NS_NewNamedThread("GMPThread", getter_AddRefs(mGMPThread));
if (NS_FAILED(rv)) {
return rv;
}
// Tell the thread to initialize plugins
mGMPThread->Dispatch(NS_NewRunnableMethod(this, &GeckoMediaPluginService::LoadFromEnvironment), NS_DISPATCH_NORMAL);
}
NS_ADDREF(mGMPThread);
*aThread = mGMPThread;
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
const nsAString& aOrigin,
GMPAudioDecoderProxy** aGMPAD)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aGMPAD);
if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE;
}
nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aOrigin,
NS_LITERAL_CSTRING("decode-audio"),
*aTags);
if (!gmp) {
return NS_ERROR_FAILURE;
}
GMPAudioDecoderParent* gmpADP;
nsresult rv = gmp->GetGMPAudioDecoder(&gmpADP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPAD = gmpADP;
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
const nsAString& aOrigin,
GMPVideoHost** aOutVideoHost,
GMPVideoDecoderProxy** aGMPVD)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aOutVideoHost);
NS_ENSURE_ARG(aGMPVD);
if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE;
}
nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aOrigin,
NS_LITERAL_CSTRING("decode-video"),
*aTags);
#ifdef PR_LOGGING
nsCString api = (*aTags)[0];
LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
#endif
if (!gmp) {
return NS_ERROR_FAILURE;
}
GMPVideoDecoderParent* gmpVDP;
nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPVD = gmpVDP;
*aOutVideoHost = &gmpVDP->Host();
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
const nsAString& aOrigin,
GMPVideoHost** aOutVideoHost,
GMPVideoEncoderProxy** aGMPVE)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aOutVideoHost);
NS_ENSURE_ARG(aGMPVE);
if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE;
}
nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aOrigin,
NS_LITERAL_CSTRING("encode-video"),
*aTags);
#ifdef PR_LOGGING
nsCString api = (*aTags)[0];
LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
#endif
if (!gmp) {
return NS_ERROR_FAILURE;
}
GMPVideoEncoderParent* gmpVEP;
nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPVE = gmpVEP;
*aOutVideoHost = &gmpVEP->Host();
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
const nsAString& aOrigin,
GMPDecryptorProxy** aDecryptor)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aDecryptor);
if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE;
}
nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aOrigin,
NS_LITERAL_CSTRING("eme-decrypt"),
*aTags);
if (!gmp) {
return NS_ERROR_FAILURE;
}
GMPDecryptorParent* ksp;
nsresult rv = gmp->GetGMPDecryptor(&ksp);
if (NS_FAILED(rv)) {
return rv;
}
*aDecryptor = static_cast<GMPDecryptorProxy*>(ksp);
return NS_OK;
}
void
GeckoMediaPluginService::AsyncShutdownNeeded(GMPParent* aParent)
{
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
mAsyncShutdownPlugins.AppendElement(aParent);
}
void
GeckoMediaPluginService::AsyncShutdownComplete(GMPParent* aParent)
{
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
mAsyncShutdownPlugins.RemoveElement(aParent);
if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) {
// The main thread is waiting for async shutdown of plugins,
// which has completed. Break the main thread out of its waiting loop.
AbortAsyncShutdown();
}
}
void
GeckoMediaPluginService::SetAsyncShutdownComplete()
{
MOZ_ASSERT(NS_IsMainThread());
mWaitingForPluginsAsyncShutdown = false;
}
void
GeckoMediaPluginService::AbortAsyncShutdown()
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
for (size_t i = 0; i < mAsyncShutdownPlugins.Length(); i++) {
mAsyncShutdownPlugins[i]->AbortAsyncShutdown();
}
mAsyncShutdownPlugins.Clear();
if (mAsyncShutdownTimeout) {
mAsyncShutdownTimeout->Cancel();
mAsyncShutdownTimeout = nullptr;
}
nsRefPtr<nsIRunnable> task(NS_NewRunnableMethod(
this, &GeckoMediaPluginService::SetAsyncShutdownComplete));
NS_DispatchToMainThread(task);
}
nsresult
GeckoMediaPluginService::SetAsyncShutdownTimeout()
{
MOZ_ASSERT(!mAsyncShutdownTimeout);
nsresult rv;
mAsyncShutdownTimeout = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create timer for async GMP shutdown");
return NS_OK;
}
// Set timer to abort waiting for plugins to shutdown if they take
// too long.
rv = mAsyncShutdownTimeout->SetTarget(mGMPThread);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return mAsyncShutdownTimeout->InitWithFuncCallback(
&AbortWaitingForGMPAsyncShutdown, nullptr, sMaxAsyncShutdownWaitMs,
nsITimer::TYPE_ONE_SHOT);
}
void
GeckoMediaPluginService::UnloadPlugins()
{
LOGD(("%s::%s async_shutdown=%d", __CLASS__, __FUNCTION__,
mAsyncShutdownPlugins.Length()));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
MOZ_ASSERT(!mShuttingDownOnGMPThread);
mShuttingDownOnGMPThread = true;
{
MutexAutoLock lock(mMutex);
// Note: CloseActive is async; it will actually finish
// shutting down when all the plugins have unloaded.
for (uint32_t i = 0; i < mPlugins.Length(); i++) {
mPlugins[i]->CloseActive(true);
}
mPlugins.Clear();
}
if (!mAsyncShutdownPlugins.IsEmpty()) {
// We have plugins that require async shutdown. Set a timer to abort
// waiting if they take too long to shutdown.
if (NS_FAILED(SetAsyncShutdownTimeout())) {
mAsyncShutdownPlugins.Clear();
}
}
if (mAsyncShutdownPlugins.IsEmpty()) {
mAsyncShutdownPlugins.Clear();
nsRefPtr<nsIRunnable> task(NS_NewRunnableMethod(
this, &GeckoMediaPluginService::SetAsyncShutdownComplete));
NS_DispatchToMainThread(task);
}
}
void
GeckoMediaPluginService::CrashPlugins()
{
LOGD(("%s::%s", __CLASS__, __FUNCTION__));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
MutexAutoLock lock(mMutex);
for (uint32_t i = 0; i < mPlugins.Length(); i++) {
mPlugins[i]->Crash();
}
}
void
GeckoMediaPluginService::LoadFromEnvironment()
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
const char* env = PR_GetEnv("MOZ_GMP_PATH");
if (!env || !*env) {
return;
}
nsString allpaths;
if (NS_WARN_IF(NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(env), allpaths)))) {
return;
}
uint32_t pos = 0;
while (pos < allpaths.Length()) {
// Loop over multiple path entries separated by colons (*nix) or
// semicolons (Windows)
int32_t next = allpaths.FindChar(XPCOM_ENV_PATH_SEPARATOR[0], pos);
if (next == -1) {
AddOnGMPThread(nsDependentSubstring(allpaths, pos));
break;
} else {
AddOnGMPThread(nsDependentSubstring(allpaths, pos, next - pos));
pos = next + 1;
}
}
}
NS_IMETHODIMP
GeckoMediaPluginService::PathRunnable::Run()
{
if (mAdd) {
mService->AddOnGMPThread(mPath);
} else {
mService->RemoveOnGMPThread(mPath);
}
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginService::AddPluginDirectory(const nsAString& aDirectory)
{
MOZ_ASSERT(NS_IsMainThread());
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
if (!mozilla::CanSandboxMediaPlugin()) {
return NS_ERROR_NOT_AVAILABLE;
}
#endif
nsCOMPtr<nsIThread> thread;
nsresult rv = GetThread(getter_AddRefs(thread));
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIRunnable> r = new PathRunnable(this, aDirectory, true);
thread->Dispatch(r, NS_DISPATCH_NORMAL);
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginService::RemovePluginDirectory(const nsAString& aDirectory)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIThread> thread;
nsresult rv = GetThread(getter_AddRefs(thread));
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIRunnable> r = new PathRunnable(this, aDirectory, false);
thread->Dispatch(r, NS_DISPATCH_NORMAL);
return NS_OK;
}
NS_IMETHODIMP
GeckoMediaPluginService::HasPluginForAPI(const nsAString& aOrigin,
const nsACString& aAPI,
nsTArray<nsCString>* aTags,
bool* aResult)
{
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aResult);
nsCString temp(aAPI);
GMPParent *parent = SelectPluginForAPI(aOrigin, temp, *aTags);
*aResult = !!parent;
return NS_OK;
}
GMPParent*
GeckoMediaPluginService::SelectPluginForAPI(const nsAString& aOrigin,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags)
{
MutexAutoLock lock(mMutex);
for (uint32_t i = 0; i < mPlugins.Length(); i++) {
GMPParent* gmp = mPlugins[i];
bool supportsAllTags = true;
for (uint32_t t = 0; t < aTags.Length(); t++) {
const nsCString& tag = aTags[t];
if (!gmp->SupportsAPI(aAPI, tag)) {
supportsAllTags = false;
break;
}
}
if (!supportsAllTags) {
continue;
}
if (aOrigin.IsEmpty()) {
if (gmp->CanBeSharedCrossOrigin()) {
return gmp;
}
} else if (gmp->CanBeUsedFrom(aOrigin)) {
if (!aOrigin.IsEmpty()) {
gmp->SetOrigin(aOrigin);
}
return gmp;
}
}
return nullptr;
}
class CreateGMPParentTask : public nsRunnable {
public:
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
mParent = new GMPParent();
return NS_OK;
}
already_AddRefed<GMPParent> GetParent() {
return mParent.forget();
}
private:
nsRefPtr<GMPParent> mParent;
};
GMPParent*
GeckoMediaPluginService::ClonePlugin(const GMPParent* aOriginal)
{
MOZ_ASSERT(aOriginal);
// The GMPParent inherits from IToplevelProtocol, which must be created
// on the main thread to be threadsafe. See Bug 1035653.
nsRefPtr<CreateGMPParentTask> task(new CreateGMPParentTask());
if (!NS_IsMainThread()) {
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
mozilla::SyncRunnable::DispatchToThread(mainThread, task);
}
nsRefPtr<GMPParent> gmp = task->GetParent();
nsresult rv = gmp->CloneFrom(aOriginal);
if (NS_FAILED(rv)) {
NS_WARNING("Can't Create GMPParent");
return nullptr;
}
MutexAutoLock lock(mMutex);
mPlugins.AppendElement(gmp);
return gmp.get();
}
void
GeckoMediaPluginService::AddOnGMPThread(const nsAString& aDirectory)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
nsCOMPtr<nsIFile> directory;
nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// The GMPParent inherits from IToplevelProtocol, which must be created
// on the main thread to be threadsafe. See Bug 1035653.
nsRefPtr<CreateGMPParentTask> task(new CreateGMPParentTask());
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
mozilla::SyncRunnable::DispatchToThread(mainThread, task);
nsRefPtr<GMPParent> gmp = task->GetParent();
rv = gmp->Init(this, directory);
if (NS_FAILED(rv)) {
NS_WARNING("Can't Create GMPParent");
return;
}
MutexAutoLock lock(mMutex);
mPlugins.AppendElement(gmp);
}
void
GeckoMediaPluginService::RemoveOnGMPThread(const nsAString& aDirectory)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
nsCOMPtr<nsIFile> directory;
nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
MutexAutoLock lock(mMutex);
for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory();
bool equals;
if (NS_SUCCEEDED(directory->Equals(pluginpath, &equals)) && equals) {
mPlugins[i]->CloseActive(true);
mPlugins.RemoveElementAt(i);
return;
}
}
NS_WARNING("Removing GMP which was never added.");
nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
cs->LogStringMessage(MOZ_UTF16("Removing GMP which was never added."));
}
// May remove when Bug 1043671 is fixed
static void Dummy(nsRefPtr<GMPParent>& aOnDeathsDoor)
{
// exists solely to do nothing and let the Runnable kill the GMPParent
// when done.
}
void
GeckoMediaPluginService::ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, (void*) aOld));
nsRefPtr<GMPParent> gmp;
if (!mShuttingDownOnGMPThread) {
// Don't re-add plugin if we're shutting down. Let the old plugin die.
gmp = ClonePlugin(aOld);
}
// Note: both are now in the list
// Until we give up the GMPThread, we're safe even if we unlock temporarily
// since off-main-thread users just test for existance; they don't modify the list.
MutexAutoLock lock(mMutex);
mPlugins.RemoveElement(aOld);
// Schedule aOld to be destroyed. We can't destroy it from here since we
// may be inside ActorDestroyed() for it.
NS_DispatchToCurrentThread(WrapRunnableNM(&Dummy, aOld));
}
} // namespace gmp
} // namespace mozilla