Bug 1331829 - Remove async shutdown GMP API. r=gerald

Now that we're not supporting Adobe EME anymore, we don't need to
provide a mechanism for GMPs to block browser shutdown.

MozReview-Commit-ID: KUC94IBQiod

--HG--
extra : rebase_source : ed521f28e272de11b2d0c4546b98baf6bd7c6e72
This commit is contained in:
Chris Pearce 2017-01-18 15:01:56 +13:00
parent 2b4c7c9921
commit abf5ab3771
16 changed files with 15 additions and 724 deletions

View File

@ -101,7 +101,6 @@ private:
// PlatformDecoderModule
DECL_MEDIA_PREF("media.apple.forcevda", AppleForceVDA, bool, false);
DECL_MEDIA_PREF("media.gmp.insecure.allow", GMPAllowInsecure, bool, false);
DECL_MEDIA_PREF("media.gmp.async-shutdown-timeout", GMPAsyncShutdownTimeout, uint32_t, GMP_DEFAULT_ASYNC_SHUTDOWN_TIMEOUT);
DECL_MEDIA_PREF("media.eme.enabled", EMEEnabled, bool, false);
DECL_MEDIA_PREF("media.use-blank-decoder", PDMUseBlankDecoder, bool, false);
DECL_MEDIA_PREF("media.gpu-process-decoder", PDMUseGPUDecoder, bool, false);

View File

@ -412,9 +412,6 @@ extern "C" {
} else if (!strcmp (aApiName, GMP_API_DECRYPTOR)) {
*aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
return GMPNoErr;
} else if (!strcmp (aApiName, GMP_API_ASYNC_SHUTDOWN)) {
*aPluginApi = new TestAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
return GMPNoErr;
#endif
}
return GMPGenericErr;

View File

@ -75,9 +75,6 @@ extern "C" {
} else if (!strcmp (aApiName, GMP_API_DECRYPTOR)) {
*aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
return GMPNoErr;
} else if (!strcmp (aApiName, GMP_API_ASYNC_SHUTDOWN)) {
*aPluginApi = new TestAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
return GMPNoErr;
#endif
}
return GMPGenericErr;

View File

@ -573,36 +573,3 @@ FakeDecryptor::UpdateSession(uint32_t aPromiseId,
Message("node-id " + sNodeId);
}
}
class CompleteShutdownTask : public GMPTask {
public:
explicit CompleteShutdownTask(GMPAsyncShutdownHost* aHost)
: mHost(aHost)
{
}
void Run() override {
mHost->ShutdownComplete();
}
void Destroy() override { delete this; }
GMPAsyncShutdownHost* mHost;
};
void
TestAsyncShutdown::BeginShutdown() {
switch (sShutdownMode) {
case ShutdownNormal:
mHost->ShutdownComplete();
break;
case ShutdownTimeout:
// Don't do anything; wait for timeout, Gecko should kill
// the plugin and recover.
break;
case ShutdownStoreToken:
// Store message, then shutdown.
WriteRecord("shutdown-token",
sShutdownToken,
new CompleteShutdownTask(mHost),
new SendMessageTask("FAIL writing shutdown-token."));
break;
}
}

View File

@ -7,7 +7,6 @@
#define FAKE_DECRYPTOR_H__
#include "gmp-decryption.h"
#include "gmp-async-shutdown.h"
#include <string>
#include "mozilla/Attributes.h"
@ -91,15 +90,4 @@ private:
GMPDecryptorHost* mHost;
};
class TestAsyncShutdown : public GMPAsyncShutdown {
public:
explicit TestAsyncShutdown(GMPAsyncShutdownHost* aHost)
: mHost(aHost)
{
}
void BeginShutdown() override;
private:
GMPAsyncShutdownHost* mHost;
};
#endif

View File

@ -53,8 +53,7 @@ extern LogModule* GetGMPLog();
namespace gmp {
GMPChild::GMPChild()
: mAsyncShutdown(nullptr)
, mGMPMessageLoop(MessageLoop::current())
: mGMPMessageLoop(MessageLoop::current())
, mGMPLoader(nullptr)
{
LOGD("GMPChild ctor");
@ -399,14 +398,6 @@ GMPChild::AnswerStartPlugin(const nsString& aAdapter)
return IPC_FAIL_NO_REASON(this);
}
void* sh = nullptr;
GMPAsyncShutdownHost* host = static_cast<GMPAsyncShutdownHost*>(this);
GMPErr err = GetAPI(GMP_API_ASYNC_SHUTDOWN, host, &sh);
if (err == GMPNoErr && sh) {
mAsyncShutdown = reinterpret_cast<GMPAsyncShutdown*>(sh);
SendAsyncShutdownRequired();
}
return IPC_OK();
}
@ -533,20 +524,6 @@ GMPChild::RecvCrashPluginNow()
return IPC_OK();
}
mozilla::ipc::IPCResult
GMPChild::RecvBeginAsyncShutdown()
{
LOGD("%s AsyncShutdown=%d", __FUNCTION__, mAsyncShutdown!=nullptr);
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
if (mAsyncShutdown) {
mAsyncShutdown->BeginShutdown();
} else {
ShutdownComplete();
}
return IPC_OK();
}
mozilla::ipc::IPCResult
GMPChild::RecvCloseActive()
{
@ -556,15 +533,6 @@ GMPChild::RecvCloseActive()
return IPC_OK();
}
void
GMPChild::ShutdownComplete()
{
LOGD("%s", __FUNCTION__);
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
mAsyncShutdown = nullptr;
SendAsyncShutdownComplete();
}
static void
GetPluginVoucherFile(const nsAString& aPluginPath,
nsCOMPtr<nsIFile>& aOutVoucherFile)

View File

@ -10,7 +10,6 @@
#include "GMPTimerChild.h"
#include "GMPStorageChild.h"
#include "GMPLoader.h"
#include "gmp-async-shutdown.h"
#include "gmp-entrypoints.h"
#include "prlink.h"
@ -20,7 +19,6 @@ namespace gmp {
class GMPContentChild;
class GMPChild : public PGMPChild
, public GMPAsyncShutdownHost
{
public:
GMPChild();
@ -37,9 +35,6 @@ public:
GMPTimerChild* GetGMPTimers();
GMPStorageChild* GetGMPStorage();
// GMPAsyncShutdownHost
void ShutdownComplete() override;
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
bool SetMacSandboxInfo(MacSandboxPluginType aPluginType);
#endif
@ -70,7 +65,6 @@ private:
void GMPContentChildActorDestroy(GMPContentChild* aGMPContentChild);
mozilla::ipc::IPCResult RecvCrashPluginNow() override;
mozilla::ipc::IPCResult RecvBeginAsyncShutdown() override;
mozilla::ipc::IPCResult RecvCloseActive() override;
void ActorDestroy(ActorDestroyReason aWhy) override;
@ -80,7 +74,6 @@ private:
nsTArray<UniquePtr<GMPContentChild>> mGMPContentChildren;
GMPAsyncShutdown* mAsyncShutdown;
RefPtr<GMPTimerChild> mTimerChild;
RefPtr<GMPStorageChild> mStorage;

View File

@ -68,8 +68,6 @@ GMPParent::GMPParent()
, mIsBlockingDeletion(false)
, mCanDecrypt(false)
, mGMPContentChildCount(0)
, mAsyncShutdownRequired(false)
, mAsyncShutdownInProgress(false)
, mChildPid(0)
, mHoldingSelfRef(false)
{
@ -217,75 +215,13 @@ GMPParent::LoadProcess()
return NS_OK;
}
// static
void
GMPParent::AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure)
{
NS_WARNING("Timed out waiting for GMP async shutdown!");
GMPParent* parent = reinterpret_cast<GMPParent*>(aClosure);
MOZ_ASSERT(parent->mService);
#if defined(MOZ_CRASHREPORTER)
parent->mService->SetAsyncShutdownPluginState(parent, 'G',
NS_LITERAL_CSTRING("Timed out waiting for async shutdown"));
#endif
parent->mService->AsyncShutdownComplete(parent);
}
nsresult
GMPParent::EnsureAsyncShutdownTimeoutSet()
{
MOZ_ASSERT(mAsyncShutdownRequired);
if (mAsyncShutdownTimeout) {
return NS_OK;
}
nsresult rv;
mAsyncShutdownTimeout = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Set timer to abort waiting for plugin to shutdown if it takes
// too long.
rv = mAsyncShutdownTimeout->SetTarget(mGMPThread);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
int32_t timeout = MediaPrefs::GMPAsyncShutdownTimeout();
RefPtr<GeckoMediaPluginServiceParent> service =
GeckoMediaPluginServiceParent::GetSingleton();
if (service) {
timeout = service->AsyncShutdownTimeoutMs();
}
rv = mAsyncShutdownTimeout->InitWithFuncCallback(
&AbortWaitingForGMPAsyncShutdown, this, timeout,
nsITimer::TYPE_ONE_SHOT);
Unused << NS_WARN_IF(NS_FAILED(rv));
return rv;
}
mozilla::ipc::IPCResult
GMPParent::RecvPGMPContentChildDestroyed()
{
--mGMPContentChildCount;
if (!IsUsed()) {
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'E',
NS_LITERAL_CSTRING("Last content child destroyed"));
}
#endif
CloseIfUnused();
}
#if defined(MOZ_CRASHREPORTER)
else {
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'F',
nsPrintfCString("Content child destroyed, remaining: %u", mGMPContentChildCount));
}
}
#endif
return IPC_OK();
}
@ -293,7 +229,7 @@ void
GMPParent::CloseIfUnused()
{
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
LOGD("%s: mAsyncShutdownRequired=%d", __FUNCTION__, mAsyncShutdownRequired);
LOGD("%s", __FUNCTION__);
if ((mDeleteProcessOnlyOnUnload ||
mState == GMPStateLoaded ||
@ -304,74 +240,15 @@ GMPParent::CloseIfUnused()
mTimers[i - 1]->Shutdown();
}
if (mAsyncShutdownRequired) {
if (!mAsyncShutdownInProgress) {
LOGD("%s: sending async shutdown notification", __FUNCTION__);
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'H',
NS_LITERAL_CSTRING("Sent BeginAsyncShutdown"));
}
#endif
mAsyncShutdownInProgress = true;
if (!SendBeginAsyncShutdown()) {
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'I',
NS_LITERAL_CSTRING("Could not send BeginAsyncShutdown - Aborting async shutdown"));
}
#endif
AbortAsyncShutdown();
} else if (NS_FAILED(EnsureAsyncShutdownTimeoutSet())) {
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'J',
NS_LITERAL_CSTRING("Could not start timer after sending BeginAsyncShutdown - Aborting async shutdown"));
}
#endif
AbortAsyncShutdown();
}
}
} else {
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'K',
NS_LITERAL_CSTRING("No (more) async-shutdown required"));
}
#endif
// No async-shutdown, kill async-shutdown timer started in CloseActive().
AbortAsyncShutdown();
// Any async shutdown must be complete. Shutdown GMPStorage.
for (size_t i = mStorage.Length(); i > 0; i--) {
mStorage[i - 1]->Shutdown();
}
Shutdown();
// Shutdown GMPStorage. Given that all protocol actors must be shutdown
// (!Used() is true), all storage operations should be complete.
for (size_t i = mStorage.Length(); i > 0; i--) {
mStorage[i - 1]->Shutdown();
}
Shutdown();
}
}
void
GMPParent::AbortAsyncShutdown()
{
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
LOGD("%s", __FUNCTION__);
if (mAsyncShutdownTimeout) {
mAsyncShutdownTimeout->Cancel();
mAsyncShutdownTimeout = nullptr;
}
if (!mAsyncShutdownRequired || !mAsyncShutdownInProgress) {
return;
}
RefPtr<GMPParent> kungFuDeathGrip(this);
mService->AsyncShutdownComplete(this);
mAsyncShutdownRequired = false;
mAsyncShutdownInProgress = false;
CloseIfUnused();
}
void
GMPParent::CloseActive(bool aDieWhenUnloaded)
{
@ -385,47 +262,8 @@ GMPParent::CloseActive(bool aDieWhenUnloaded)
mState = GMPStateUnloading;
}
if (mState != GMPStateNotLoaded && IsUsed()) {
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'A',
nsPrintfCString("Sent CloseActive, content children to close: %u", mGMPContentChildCount));
}
#endif
if (!SendCloseActive()) {
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'B',
NS_LITERAL_CSTRING("Could not send CloseActive - Aborting async shutdown"));
}
#endif
AbortAsyncShutdown();
} else if (IsUsed()) {
// We're expecting RecvPGMPContentChildDestroyed's -> Start async-shutdown timer now if needed.
if (mAsyncShutdownRequired && NS_FAILED(EnsureAsyncShutdownTimeoutSet())) {
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'C',
NS_LITERAL_CSTRING("Could not start timer after sending CloseActive - Aborting async shutdown"));
}
#endif
AbortAsyncShutdown();
}
} else {
// We're not expecting any RecvPGMPContentChildDestroyed
// -> Call CloseIfUnused() now, to run async shutdown if necessary.
// Note that CloseIfUnused() may have already been called from a prior
// RecvPGMPContentChildDestroyed(), however depending on the state at
// that time, it might not have proceeded with shutdown; And calling it
// again after shutdown is fine because after the first one we'll be in
// GMPStateNotLoaded.
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'D',
NS_LITERAL_CSTRING("Content children already destroyed"));
}
#endif
CloseIfUnused();
}
Unused << SendCloseActive();
CloseIfUnused();
}
}
@ -448,8 +286,6 @@ GMPParent::Shutdown()
LOGD("%s", __FUNCTION__);
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
MOZ_ASSERT(!mAsyncShutdownTimeout, "Should have canceled shutdown timeout");
if (mAbnormalShutdownInProgress) {
return;
}
@ -706,16 +542,6 @@ GMPParent::ActorDestroy(ActorDestroyReason aWhy)
// Normal Shutdown() will delete the process on unwind.
if (AbnormalShutdown == aWhy) {
RefPtr<GMPParent> self(this);
if (mAsyncShutdownRequired) {
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'M',
NS_LITERAL_CSTRING("Actor destroyed"));
}
#endif
mService->AsyncShutdownComplete(this);
mAsyncShutdownRequired = false;
}
// Must not call Close() again in DeleteProcess(), as we'll recurse
// infinitely if we do.
MOZ_ASSERT(mState == GMPStateClosing);
@ -998,8 +824,7 @@ GMPParent::ParseChromiumManifest(const nsAString& aJSON)
bool
GMPParent::CanBeSharedCrossNodeIds() const
{
return !mAsyncShutdownInProgress &&
mNodeId.IsEmpty() &&
return mNodeId.IsEmpty() &&
// XXX bug 1159300 hack -- maybe remove after openh264 1.4
// We don't want to use CDM decoders for non-encrypted playback
// just yet; especially not for WebRTC. Don't allow CDMs to be used
@ -1010,7 +835,7 @@ GMPParent::CanBeSharedCrossNodeIds() const
bool
GMPParent::CanBeUsedFrom(const nsACString& aNodeId) const
{
return !mAsyncShutdownInProgress && mNodeId == aNodeId;
return mNodeId == aNodeId;
}
void
@ -1038,35 +863,6 @@ GMPParent::GetPluginId() const
return mPluginId;
}
mozilla::ipc::IPCResult
GMPParent::RecvAsyncShutdownRequired()
{
LOGD("%s", __FUNCTION__);
if (mAsyncShutdownRequired) {
NS_WARNING("Received AsyncShutdownRequired message more than once!");
return IPC_OK();
}
mAsyncShutdownRequired = true;
mService->AsyncShutdownNeeded(this);
return IPC_OK();
}
mozilla::ipc::IPCResult
GMPParent::RecvAsyncShutdownComplete()
{
LOGD("%s", __FUNCTION__);
MOZ_ASSERT(mAsyncShutdownRequired);
#if defined(MOZ_CRASHREPORTER)
if (mService) {
mService->SetAsyncShutdownPluginState(this, 'L',
NS_LITERAL_CSTRING("Received AsyncShutdownComplete"));
}
#endif
AbortAsyncShutdown();
return IPC_OK();
}
void
GMPParent::ResolveGetContentParentPromises()
{

View File

@ -182,9 +182,6 @@ private:
PGMPTimerParent* AllocPGMPTimerParent() override;
bool DeallocPGMPTimerParent(PGMPTimerParent* aActor) override;
mozilla::ipc::IPCResult RecvAsyncShutdownComplete() override;
mozilla::ipc::IPCResult RecvAsyncShutdownRequired() override;
mozilla::ipc::IPCResult RecvPGMPContentChildDestroyed() override;
bool IsUsed()
{
@ -195,9 +192,6 @@ private:
void ResolveGetContentParentPromises();
void RejectGetContentParentPromises();
static void AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure);
nsresult EnsureAsyncShutdownTimeoutSet();
GMPState mState;
nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
nsString mName; // base name of plugin on disk, UTF-16 because used for paths
@ -220,7 +214,6 @@ private:
nsTArray<RefPtr<GMPTimerParent>> mTimers;
nsTArray<RefPtr<GMPStorageParent>> mStorage;
nsCOMPtr<nsIThread> mGMPThread;
nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
// NodeId the plugin is assigned to, or empty if the the plugin is not
// assigned to a NodeId.
nsCString mNodeId;
@ -230,9 +223,6 @@ private:
nsTArray<UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>>> mGetContentParentPromises;
uint32_t mGMPContentChildCount;
bool mAsyncShutdownRequired;
bool mAsyncShutdownInProgress;
int mChildPid;
// We hold a self reference to ourself while the child process is alive.

View File

@ -75,8 +75,6 @@ public:
return GetDecryptingGMPVideoDecoder(aHelper, aTags, aNodeId, Move(aCallback), 0);
}
int32_t AsyncShutdownTimeoutMs();
NS_IMETHOD RunPluginCrashCallbacks(uint32_t aPluginId,
const nsACString& aPluginName) override;

View File

@ -87,9 +87,6 @@ NS_IMPL_ISUPPORTS_INHERITED(GeckoMediaPluginServiceParent,
GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
: mShuttingDown(false)
#ifdef MOZ_CRASHREPORTER
, mAsyncShutdownPluginStatesMutex("GeckoMediaPluginService::mAsyncShutdownPluginStatesMutex")
#endif
, mScannedPluginOnDisk(false)
, mWaitingForPluginsSyncShutdown(false)
, mInitPromiseMonitor("GeckoMediaPluginServiceParent::mInitPromiseMonitor")
@ -103,13 +100,6 @@ GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
GeckoMediaPluginServiceParent::~GeckoMediaPluginServiceParent()
{
MOZ_ASSERT(mPlugins.IsEmpty());
MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty());
}
int32_t
GeckoMediaPluginServiceParent::AsyncShutdownTimeoutMs()
{
return MediaPrefs::GMPAsyncShutdownTimeout();
}
nsresult
@ -288,48 +278,6 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
}
}
} 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 mWaitingForPluginsSyncShutdown to true, and then
// call UnloadPlugins on the GMPThread, and process events on the main
// thread until 1. An event sets mWaitingForPluginsSyncShutdown=false on
// the main thread; then 2. All async-shutdown plugins have indicated
// they have completed shutdown.
//
// 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 a dummy event on
// the main thread, where the list of pending plugins is checked. 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.
//
// During shutdown, each GMPParent starts a timer, and pretends shutdown
// is complete if it is taking too long.
//
// 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.
mWaitingForPluginsSyncShutdown = true;
nsCOMPtr<nsIThread> gmpThread;
@ -341,68 +289,18 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
}
if (gmpThread) {
LOGD(("%s::%s Starting to unload plugins, waiting for first sync shutdown..."
LOGD(("%s::%s Starting to unload plugins, waiting for sync shutdown..."
, __CLASS__, __FUNCTION__));
#ifdef MOZ_CRASHREPORTER
SetAsyncShutdownPluginState(nullptr, '0',
NS_LITERAL_CSTRING("Dispatching UnloadPlugins"));
#endif
gmpThread->Dispatch(
NewRunnableMethod(this,
&GeckoMediaPluginServiceParent::UnloadPlugins),
NS_DISPATCH_NORMAL);
#ifdef MOZ_CRASHREPORTER
SetAsyncShutdownPluginState(nullptr, '1',
NS_LITERAL_CSTRING("Waiting for sync shutdown"));
#endif
// Wait for UnloadPlugins() to do initial sync shutdown...
// Wait for UnloadPlugins() to do sync shutdown...
while (mWaitingForPluginsSyncShutdown) {
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
}
#ifdef MOZ_CRASHREPORTER
SetAsyncShutdownPluginState(nullptr, '4',
NS_LITERAL_CSTRING("Waiting for async shutdown"));
#endif
// Wait for other plugins (if any) to do async shutdown...
auto syncShutdownPluginsRemaining =
std::numeric_limits<decltype(mAsyncShutdownPlugins.Length())>::max();
for (;;) {
{
MutexAutoLock lock(mMutex);
if (mAsyncShutdownPlugins.IsEmpty()) {
LOGD(("%s::%s Finished unloading all plugins"
, __CLASS__, __FUNCTION__));
#if defined(MOZ_CRASHREPORTER)
CrashReporter::RemoveCrashReportAnnotation(
NS_LITERAL_CSTRING("AsyncPluginShutdown"));
#endif
break;
} else if (mAsyncShutdownPlugins.Length() < syncShutdownPluginsRemaining) {
// First time here, or number of pending plugins has decreased.
// -> Update list of pending plugins in crash report.
syncShutdownPluginsRemaining = mAsyncShutdownPlugins.Length();
LOGD(("%s::%s Still waiting for %d plugins to shutdown..."
, __CLASS__, __FUNCTION__, (int)syncShutdownPluginsRemaining));
#if defined(MOZ_CRASHREPORTER)
nsAutoCString names;
for (const auto& plugin : mAsyncShutdownPlugins) {
if (!names.IsEmpty()) { names.Append(NS_LITERAL_CSTRING(", ")); }
names.Append(plugin->GetDisplayName());
}
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("AsyncPluginShutdown"),
names);
#endif
}
}
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
}
#ifdef MOZ_CRASHREPORTER
SetAsyncShutdownPluginState(nullptr, '5',
NS_LITERAL_CSTRING("Async shutdown complete"));
#endif
} else {
// GMP thread has already shutdown.
MOZ_ASSERT(mPlugins.IsEmpty());
@ -519,105 +417,6 @@ GeckoMediaPluginServiceParent::InitializePlugins(
});
}
void
GeckoMediaPluginServiceParent::AsyncShutdownNeeded(GMPParent* aParent)
{
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mAsyncShutdownPlugins.Contains(aParent));
mAsyncShutdownPlugins.AppendElement(aParent);
}
void
GeckoMediaPluginServiceParent::AsyncShutdownComplete(GMPParent* aParent)
{
LOGD(("%s::%s %p '%s'", __CLASS__, __FUNCTION__,
aParent, aParent->GetDisplayName().get()));
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
{
MutexAutoLock lock(mMutex);
mAsyncShutdownPlugins.RemoveElement(aParent);
}
if (mShuttingDownOnGMPThread) {
// The main thread may be waiting for async shutdown of plugins,
// one of which has completed. Wake up the main thread by sending a task.
nsCOMPtr<nsIRunnable> task(NewRunnableMethod(
this, &GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete));
NS_DispatchToMainThread(task);
}
}
#ifdef MOZ_CRASHREPORTER
void
GeckoMediaPluginServiceParent::SetAsyncShutdownPluginState(GMPParent* aGMPParent,
char aId,
const nsCString& aState)
{
MutexAutoLock lock(mAsyncShutdownPluginStatesMutex);
if (!aGMPParent) {
mAsyncShutdownPluginStates.Update(NS_LITERAL_CSTRING("-"),
NS_LITERAL_CSTRING("-"),
aId,
aState);
return;
}
mAsyncShutdownPluginStates.Update(aGMPParent->GetDisplayName(),
nsPrintfCString("%p", aGMPParent),
aId,
aState);
}
void
GeckoMediaPluginServiceParent::AsyncShutdownPluginStates::Update(const nsCString& aPlugin,
const nsCString& aInstance,
char aId,
const nsCString& aState)
{
nsCString note;
StatesByInstance* instances = mStates.LookupOrAdd(aPlugin);
if (!instances) { return; }
State* state = instances->LookupOrAdd(aInstance);
if (!state) { return; }
state->mStateSequence += aId;
state->mLastStateDescription = aState;
note += '{';
bool firstPlugin = true;
for (auto pluginIt = mStates.ConstIter(); !pluginIt.Done(); pluginIt.Next()) {
if (!firstPlugin) { note += ','; } else { firstPlugin = false; }
note += pluginIt.Key();
note += ":{";
bool firstInstance = true;
for (auto instanceIt = pluginIt.UserData()->ConstIter(); !instanceIt.Done(); instanceIt.Next()) {
if (!firstInstance) { note += ','; } else { firstInstance = false; }
note += instanceIt.Key();
note += ":\"";
note += instanceIt.UserData()->mStateSequence;
note += '=';
note += instanceIt.UserData()->mLastStateDescription;
note += '"';
}
note += '}';
}
note += '}';
LOGD(("%s::%s states[%s][%s]='%c'/'%s' -> %s", __CLASS__, __FUNCTION__,
aPlugin.get(), aInstance.get(), aId, aState.get(), note.get()));
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("AsyncPluginShutdownStates"),
note);
}
#endif // MOZ_CRASHREPORTER
void
GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete()
{
MOZ_ASSERT(NS_IsMainThread());
// Nothing to do, this task is just used to wake up the event loop in Observe().
}
void
GeckoMediaPluginServiceParent::NotifySyncShutdownComplete()
{
@ -638,10 +437,6 @@ GeckoMediaPluginServiceParent::UnloadPlugins()
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
MOZ_ASSERT(!mShuttingDownOnGMPThread);
mShuttingDownOnGMPThread = true;
#ifdef MOZ_CRASHREPORTER
SetAsyncShutdownPluginState(nullptr, '2',
NS_LITERAL_CSTRING("Starting to unload plugins"));
#endif
nsTArray<RefPtr<GMPParent>> plugins;
{
@ -651,32 +446,20 @@ GeckoMediaPluginServiceParent::UnloadPlugins()
Swap(plugins, mPlugins);
}
LOGD(("%s::%s plugins:%u including async:%u", __CLASS__, __FUNCTION__,
plugins.Length(), mAsyncShutdownPlugins.Length()));
LOGD(("%s::%s plugins:%u", __CLASS__, __FUNCTION__,
plugins.Length()));
#ifdef DEBUG
for (const auto& plugin : plugins) {
LOGD(("%s::%s plugin: '%s'", __CLASS__, __FUNCTION__,
plugin->GetDisplayName().get()));
}
for (const auto& plugin : mAsyncShutdownPlugins) {
LOGD(("%s::%s async plugin: '%s'", __CLASS__, __FUNCTION__,
plugin->GetDisplayName().get()));
}
#endif
// Note: CloseActive may be async; it could actually finish
// shutting down when all the plugins have unloaded.
for (const auto& plugin : plugins) {
#ifdef MOZ_CRASHREPORTER
SetAsyncShutdownPluginState(plugin, 'S',
NS_LITERAL_CSTRING("CloseActive"));
#endif
plugin->CloseActive(true);
}
#ifdef MOZ_CRASHREPORTER
SetAsyncShutdownPluginState(nullptr, '3',
NS_LITERAL_CSTRING("Dispatching sync-shutdown-complete"));
#endif
nsCOMPtr<nsIRunnable> task(NewRunnableMethod(
this, &GeckoMediaPluginServiceParent::NotifySyncShutdownComplete));
NS_DispatchToMainThread(task);
@ -1118,7 +901,6 @@ GeckoMediaPluginServiceParent::RemoveOnGMPThread(const nsAString& aDirectory,
{
MutexAutoUnlock unlock(mMutex);
for (auto& gmp : deadPlugins) {
gmp->AbortAsyncShutdown();
gmp->CloseActive(true);
}
}
@ -1562,9 +1344,6 @@ KillPlugins(const nsTArray<RefPtr<GMPParent>>& aPlugins,
for (size_t i = 0; i < pluginsToKill.Length(); i++) {
pluginsToKill[i]->CloseActive(false);
// Abort async shutdown because we're going to wipe the plugin's storage,
// so we don't want it writing more data in its async shutdown path.
pluginsToKill[i]->AbortAsyncShutdown();
}
}

View File

@ -49,13 +49,6 @@ public:
NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
NS_DECL_NSIOBSERVER
void AsyncShutdownNeeded(GMPParent* aParent);
void AsyncShutdownComplete(GMPParent* aParent);
int32_t AsyncShutdownTimeoutMs();
#ifdef MOZ_CRASHREPORTER
void SetAsyncShutdownPluginState(GMPParent* aGMPParent, char aId, const nsCString& aState);
#endif // MOZ_CRASHREPORTER
RefPtr<GenericPromise> EnsureInitialized();
RefPtr<GenericPromise> AsyncAddPluginDirectory(const nsAString& aDirectory);
@ -94,7 +87,6 @@ private:
void UnloadPlugins();
void CrashPlugins();
void NotifySyncShutdownComplete();
void NotifyAsyncShutdownComplete();
void ProcessPossiblePlugin(nsIFile* aDir);
@ -102,8 +94,6 @@ private:
const bool aDeleteFromDisk,
const bool aCanDefer);
nsresult SetAsyncShutdownTimeout();
struct DirectoryFilter {
virtual bool operator()(nsIFile* aPath) = 0;
~DirectoryFilter() {}
@ -166,22 +156,6 @@ private:
// Protected by mMutex from the base class.
nsTArray<RefPtr<GMPParent>> mPlugins;
bool mShuttingDown;
nsTArray<RefPtr<GMPParent>> mAsyncShutdownPlugins;
#ifdef MOZ_CRASHREPORTER
Mutex mAsyncShutdownPluginStatesMutex; // Protects mAsyncShutdownPluginStates.
class AsyncShutdownPluginStates
{
public:
void Update(const nsCString& aPlugin, const nsCString& aInstance,
char aId, const nsCString& aState);
private:
struct State { nsCString mStateSequence; nsCString mLastStateDescription; };
typedef nsClassHashtable<nsCStringHashKey, State> StatesByInstance;
typedef nsClassHashtable<nsCStringHashKey, StatesByInstance> StateInstancesByPlugin;
StateInstancesByPlugin mStates;
} mAsyncShutdownPluginStates;
#endif // MOZ_CRASHREPORTER
// True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
// plugins found there into mPlugins.

View File

@ -28,11 +28,7 @@ parent:
async PGMPContentChildDestroyed();
async AsyncShutdownComplete();
async AsyncShutdownRequired();
child:
async BeginAsyncShutdown();
async CrashPluginNow();
intr StartPlugin(nsString adapter);
async SetNodeId(nsCString nodeId);

View File

@ -1,54 +0,0 @@
/*
* Copyright 2013, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GMP_ASYNC_SHUTDOWN_H_
#define GMP_ASYNC_SHUTDOWN_H_
#define GMP_API_ASYNC_SHUTDOWN "async-shutdown"
// API exposed by the plugin library to manage asynchronous shutdown.
// Some plugins require special cleanup which may need to make calls
// to host services and wait for async responses.
//
// To enable a plugins to block shutdown until its async shutdown is
// complete, implement the GMPAsyncShutdown interface and return it when
// your plugin's GMPGetAPI function is called with "async-shutdown".
// When your GMPAsyncShutdown's BeginShutdown() implementation is called
// by the GMP host, you should initate your async shutdown process.
// Once you have completed shutdown, call the ShutdownComplete() function
// of the GMPAsyncShutdownHost that is passed as the host argument to the
// GMPGetAPI() call.
//
// Note: Your GMP's GMPShutdown function will still be called after your
// call to ShutdownComplete().
//
// API name macro: GMP_API_ASYNC_SHUTDOWN
// Host API: GMPAsyncShutdownHost
class GMPAsyncShutdown {
public:
virtual ~GMPAsyncShutdown() {}
virtual void BeginShutdown() = 0;
};
class GMPAsyncShutdownHost {
public:
virtual ~GMPAsyncShutdownHost() {}
virtual void ShutdownComplete() = 0;
};
#endif // GMP_ASYNC_SHUTDOWN_H_

View File

@ -12,7 +12,6 @@ XPIDL_SOURCES += [
]
EXPORTS += [
'gmp-api/gmp-async-shutdown.h',
'gmp-api/gmp-decryption.h',
'gmp-api/gmp-entrypoints.h',
'gmp-api/gmp-errors.h',

View File

@ -1095,92 +1095,6 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
NS_LITERAL_CSTRING("retrieve pbdata"));
}
void NextAsyncShutdownTimeoutTest(nsIRunnable* aContinuation)
{
if (mDecryptor) {
Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
Shutdown();
}
nsCOMPtr<nsIThread> thread(GetGMPThread());
thread->Dispatch(aContinuation, NS_DISPATCH_NORMAL);
}
void CreateAsyncShutdownTimeoutGMP(const nsAString& aOrigin1,
const nsAString& aOrigin2,
void (GMPStorageTest::*aCallback)()) {
nsCOMPtr<nsIRunnable> continuation(
NewRunnableMethod<nsCOMPtr<nsIRunnable>>(
this,
&GMPStorageTest::NextAsyncShutdownTimeoutTest,
NewRunnableMethod(this, aCallback)));
CreateDecryptor(GetNodeId(aOrigin1, aOrigin2, false), continuation);
}
void TestAsyncShutdownTimeout() {
// Create decryptors that timeout in their async shutdown.
// If the gtest hangs on shutdown, test fails!
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example7.com"),
NS_LITERAL_STRING("http://example8.com"),
&GMPStorageTest::TestAsyncShutdownTimeout2);
};
void TestAsyncShutdownTimeout2() {
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example9.com"),
NS_LITERAL_STRING("http://example10.com"),
&GMPStorageTest::TestAsyncShutdownTimeout3);
};
void TestAsyncShutdownTimeout3() {
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("http://example11.com"),
NS_LITERAL_STRING("http://example12.com"),
&GMPStorageTest::SetFinished);
};
void TestAsyncShutdownStorage() {
// Instruct the GMP to write a token (the current timestamp, so it's
// unique) during async shutdown, then shutdown the plugin, re-create
// it, and check that the token was successfully stored.
auto t = time(0);
nsCString update("shutdown-mode token ");
nsCString token;
token.AppendInt((int64_t)t);
update.Append(token);
// Wait for a response from the GMP, so we know it's had time to receive
// the token.
nsCString response("shutdown-token received ");
response.Append(token);
Expect(response, NewRunnableMethod<nsCString>(this,
&GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
// Test that a GMP can write to storage during shutdown, and retrieve
// that written data in a subsequent session.
CreateDecryptor(NS_LITERAL_STRING("http://example13.com"),
NS_LITERAL_STRING("http://example14.com"),
false,
update);
}
void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
ShutdownThen(NewRunnableMethod<nsCString>(this,
&GMPStorageTest::TestAsyncShutdownStorage_AsyncShutdownComplete, aToken));
}
void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
// Create a new instance of the plugin, retrieve the token written
// during shutdown and verify it is correct.
nsCString response("retrieved shutdown-token ");
response.Append(aToken);
Expect(response,
NewRunnableMethod(this, &GMPStorageTest::SetFinished));
CreateDecryptor(NS_LITERAL_STRING("http://example13.com"),
NS_LITERAL_STRING("http://example14.com"),
false,
NS_LITERAL_CSTRING("retrieve-shutdown-token"));
}
#if defined(XP_WIN)
void TestOutputProtection() {
Shutdown();
@ -1514,16 +1428,6 @@ TEST(GeckoMediaPlugins, GMPStoragePrivateBrowsing) {
runner->DoTest(&GMPStorageTest::TestPBStorage);
}
TEST(GeckoMediaPlugins, GMPStorageAsyncShutdownTimeout) {
RefPtr<GMPStorageTest> runner = new GMPStorageTest();
runner->DoTest(&GMPStorageTest::TestAsyncShutdownTimeout);
}
TEST(GeckoMediaPlugins, GMPStorageAsyncShutdownStorage) {
RefPtr<GMPStorageTest> runner = new GMPStorageTest();
runner->DoTest(&GMPStorageTest::TestAsyncShutdownStorage);
}
TEST(GeckoMediaPlugins, GMPPluginVoucher) {
RefPtr<GMPStorageTest> runner = new GMPStorageTest();
runner->DoTest(&GMPStorageTest::TestPluginVoucher);