diff --git a/content/media/gmp/GMPChild.cpp b/content/media/gmp/GMPChild.cpp index c0ef5bede0cd..0f4c1b9966d6 100644 --- a/content/media/gmp/GMPChild.cpp +++ b/content/media/gmp/GMPChild.cpp @@ -37,7 +37,8 @@ namespace mozilla { namespace gmp { GMPChild::GMPChild() - : mLib(nullptr) + : mAsyncShutdown(nullptr) + , mLib(nullptr) , mGetAPIFunc(nullptr) , mGMPMessageLoop(MessageLoop::current()) { @@ -183,6 +184,14 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath) return false; } + void* sh = nullptr; + GMPAsyncShutdownHost* host = static_cast(this); + GMPErr err = mGetAPIFunc("async-shutdown", host, &sh); + if (err == GMPNoErr && sh) { + mAsyncShutdown = reinterpret_cast(sh); + SendAsyncShutdownRequired(); + } + return true; } @@ -397,5 +406,24 @@ GMPChild::RecvCrashPluginNow() return true; } +bool +GMPChild::RecvBeginAsyncShutdown() +{ + MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current()); + if (mAsyncShutdown) { + mAsyncShutdown->BeginShutdown(); + } else { + ShutdownComplete(); + } + return true; +} + +void +GMPChild::ShutdownComplete() +{ + MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current()); + SendAsyncShutdownComplete(); +} + } // namespace gmp } // namespace mozilla diff --git a/content/media/gmp/GMPChild.h b/content/media/gmp/GMPChild.h index d01f1b031dd5..120dc5f8f55e 100644 --- a/content/media/gmp/GMPChild.h +++ b/content/media/gmp/GMPChild.h @@ -9,14 +9,16 @@ #include "mozilla/gmp/PGMPChild.h" #include "GMPSharedMemManager.h" #include "GMPTimerChild.h" +#include "gmp-async-shutdown.h" #include "gmp-entrypoints.h" #include "prlink.h" namespace mozilla { namespace gmp { -class GMPChild : public PGMPChild, - public GMPSharedMem +class GMPChild : public PGMPChild + , public GMPSharedMem + , public GMPAsyncShutdownHost { public: GMPChild(); @@ -39,6 +41,9 @@ public: // GMPSharedMem virtual void CheckThread() MOZ_OVERRIDE; + // GMPAsyncShutdownHost + void ShutdownComplete() MOZ_OVERRIDE; + private: virtual PCrashReporterChild* AllocPCrashReporterChild(const NativeThreadId& aThread) MOZ_OVERRIDE; virtual bool DeallocPCrashReporterChild(PCrashReporterChild*) MOZ_OVERRIDE; @@ -63,10 +68,12 @@ private: virtual bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) MOZ_OVERRIDE; virtual bool RecvCrashPluginNow() MOZ_OVERRIDE; + virtual bool RecvBeginAsyncShutdown() MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; virtual void ProcessingError(Result aWhat) MOZ_OVERRIDE; + GMPAsyncShutdown* mAsyncShutdown; nsRefPtr mTimerChild; PRLibrary* mLib; diff --git a/content/media/gmp/GMPParent.cpp b/content/media/gmp/GMPParent.cpp index 7509d6c4bccf..46408d123dcb 100644 --- a/content/media/gmp/GMPParent.cpp +++ b/content/media/gmp/GMPParent.cpp @@ -55,6 +55,8 @@ GMPParent::GMPParent() , mProcess(nullptr) , mDeleteProcessOnlyOnUnload(false) , mAbnormalShutdownInProgress(false) + , mAsyncShutdownRequired(false) + , mAsyncShutdownInProgress(false) { } @@ -149,6 +151,8 @@ void GMPParent::CloseIfUnused() { MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); + LOGD(("%s::%s: %p mAsyncShutdownRequired=%d", __CLASS__, __FUNCTION__, this, + mAsyncShutdownRequired)); if ((mDeleteProcessOnlyOnUnload || mState == GMPStateLoaded || @@ -157,10 +161,34 @@ GMPParent::CloseIfUnused() mVideoEncoders.IsEmpty() && mDecryptors.IsEmpty() && mAudioDecoders.IsEmpty()) { - Shutdown(); + if (mAsyncShutdownRequired) { + if (!mAsyncShutdownInProgress) { + LOGD(("%s::%s: %p sending async shutdown notification", __CLASS__, + __FUNCTION__, this)); + mAsyncShutdownInProgress = true; + if (!SendBeginAsyncShutdown()) { + AbortAsyncShutdown(); + } + } + } else { + Shutdown(); + } } } +void +GMPParent::AbortAsyncShutdown() +{ + MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); + LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); + + nsRefPtr kungFuDeathGrip(this); + mService->AsyncShutdownComplete(this); + mAsyncShutdownRequired = false; + mAsyncShutdownInProgress = false; + CloseIfUnused(); +} + void GMPParent::AudioDecoderDestroyed(GMPAudioDecoderParent* aDecoder) { @@ -229,7 +257,6 @@ GMPParent::Shutdown() return; } - mState = GMPStateClosing; DeleteProcess(); // XXX Get rid of mDeleteProcessOnlyOnUnload and this code when // Bug 1043671 is fixed @@ -245,10 +272,13 @@ void GMPParent::DeleteProcess() { LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); - // Don't Close() twice! - // Probably remove when bug 1043671 is resolved - MOZ_ASSERT(mState == GMPStateClosing); - Close(); + + if (mState != GMPStateClosing) { + // Don't Close() twice! + // Probably remove when bug 1043671 is resolved + mState = GMPStateClosing; + Close(); + } mProcess->Delete(); LOGD(("%s::%s: Shut down process %p", __CLASS__, __FUNCTION__, (void *) mProcess)); mProcess = nullptr; @@ -532,8 +562,15 @@ GMPParent::ActorDestroy(ActorDestroyReason aWhy) // Normal Shutdown() will delete the process on unwind. if (AbnormalShutdown == aWhy) { - mState = GMPStateClosing; nsRefPtr self(this); + if (mAsyncShutdownRequired) { + mService->AsyncShutdownComplete(this); + mAsyncShutdownRequired = false; + } + // Must not call Close() again in DeleteProcess(), as we'll recurse + // infinitely if we do. + MOZ_ASSERT(mState == GMPStateClosing); + DeleteProcess(); // Note: final destruction will be Dispatched to ourself mService->ReAddOnGMPThread(self); } @@ -631,7 +668,7 @@ PGMPTimerParent* GMPParent::AllocPGMPTimerParent() { GMPTimerParent* p = new GMPTimerParent(GMPThread()); - NS_ADDREF(p); // Released in DeallocPGMPTimerParent. + mTimers.AppendElement(p); // Released in DeallocPGMPTimerParent, or on shutdown. return p; } @@ -639,7 +676,8 @@ bool GMPParent::DeallocPGMPTimerParent(PGMPTimerParent* aActor) { GMPTimerParent* p = static_cast(aActor); - NS_RELEASE(p); + p->Shutdown(); + mTimers.RemoveElement(p); return true; } @@ -806,5 +844,24 @@ GMPParent::SetOrigin(const nsAString& aOrigin) mOrigin = aOrigin; } +bool +GMPParent::RecvAsyncShutdownRequired() +{ + LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); + mAsyncShutdownRequired = true; + mService->AsyncShutdownNeeded(this); + return true; +} + +bool +GMPParent::RecvAsyncShutdownComplete() +{ + LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); + + MOZ_ASSERT(mAsyncShutdownRequired); + AbortAsyncShutdown(); + return true; +} + } // namespace gmp } // namespace mozilla diff --git a/content/media/gmp/GMPParent.h b/content/media/gmp/GMPParent.h index d21c26964700..28eca5ec451a 100644 --- a/content/media/gmp/GMPParent.h +++ b/content/media/gmp/GMPParent.h @@ -126,6 +126,8 @@ public: // GMPSharedMem virtual void CheckThread() MOZ_OVERRIDE; + void AbortAsyncShutdown(); + private: ~GMPParent(); nsRefPtr mService; @@ -156,6 +158,9 @@ private: virtual PGMPTimerParent* AllocPGMPTimerParent() MOZ_OVERRIDE; virtual bool DeallocPGMPTimerParent(PGMPTimerParent* aActor) MOZ_OVERRIDE; + virtual bool RecvAsyncShutdownComplete() MOZ_OVERRIDE; + virtual bool RecvAsyncShutdownRequired() MOZ_OVERRIDE; + GMPState mState; nsCOMPtr mDirectory; // plugin directory on disk nsString mName; // base name of plugin on disk, UTF-16 because used for paths @@ -171,10 +176,14 @@ private: nsTArray> mVideoEncoders; nsTArray> mDecryptors; nsTArray> mAudioDecoders; + nsTArray> mTimers; nsCOMPtr mGMPThread; // Origin the plugin is assigned to, or empty if the the plugin is not // assigned to an origin. nsAutoString mOrigin; + + bool mAsyncShutdownRequired; + bool mAsyncShutdownInProgress; }; } // namespace gmp diff --git a/content/media/gmp/GMPProcessParent.cpp b/content/media/gmp/GMPProcessParent.cpp index e93bb5874701..0264f4672cc9 100644 --- a/content/media/gmp/GMPProcessParent.cpp +++ b/content/media/gmp/GMPProcessParent.cpp @@ -30,10 +30,12 @@ GMPProcessParent::GMPProcessParent(const std::string& aGMPPath) : GeckoChildProcessHost(GeckoProcessType_GMPlugin), mGMPPath(aGMPPath) { + MOZ_COUNT_CTOR(GMPProcessParent); } GMPProcessParent::~GMPProcessParent() { + MOZ_COUNT_DTOR(GMPProcessParent); } bool diff --git a/content/media/gmp/GMPService.cpp b/content/media/gmp/GMPService.cpp index 762d0d73eb6b..4c6a1a2138a0 100644 --- a/content/media/gmp/GMPService.cpp +++ b/content/media/gmp/GMPService.cpp @@ -19,6 +19,8 @@ #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" @@ -127,17 +129,29 @@ GeckoMediaPluginService::GetGeckoMediaPluginService() 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 @@ -147,7 +161,7 @@ GeckoMediaPluginService::Init() nsCOMPtr obsService = mozilla::services::GetObserverService(); MOZ_ASSERT(obsService); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))); + 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 prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); @@ -160,11 +174,22 @@ GeckoMediaPluginService::Init() unused << GetThread(getter_AddRefs(thread)); } +void +AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure) +{ + NS_WARNING("Timed out waiting for GMP async shutdown!"); + nsRefPtr 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 branch( do_QueryInterface(aSubject) ); if (branch) { @@ -184,7 +209,50 @@ GeckoMediaPluginService::Observe(nsISupports* aSubject, } } } - } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) { + } 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 gmpThread; { MutexAutoLock lock(mMutex); @@ -194,11 +262,18 @@ GeckoMediaPluginService::Observe(nsISupports* aSubject, } if (gmpThread) { - gmpThread->Dispatch(NS_NewRunnableMethod(this, &GeckoMediaPluginService::UnloadPlugins), - NS_DISPATCH_SYNC); + 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 gmpThread; { @@ -383,26 +458,117 @@ GeckoMediaPluginService::GetGMPDecryptor(nsTArray* aTags, 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 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); + { + 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 task(NS_NewRunnableMethod( + this, &GeckoMediaPluginService::SetAsyncShutdownComplete)); + NS_DispatchToMainThread(task); } - mPlugins.Clear(); } void GeckoMediaPluginService::CrashPlugins() { + LOGD(("%s::%s", __CLASS__, __FUNCTION__)); MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); MutexAutoLock lock(mMutex); @@ -645,7 +811,11 @@ GeckoMediaPluginService::ReAddOnGMPThread(nsRefPtr& aOld) MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, (void*) aOld)); - nsRefPtr gmp = ClonePlugin(aOld); + nsRefPtr 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. diff --git a/content/media/gmp/GMPService.h b/content/media/gmp/GMPService.h index f9dab0be5edf..7b63994faaed 100644 --- a/content/media/gmp/GMPService.h +++ b/content/media/gmp/GMPService.h @@ -10,11 +10,12 @@ #include "mozIGeckoMediaPluginService.h" #include "nsIObserver.h" #include "nsTArray.h" -#include "mozilla/Mutex.h" +#include "mozilla/Monitor.h" #include "nsString.h" #include "nsCOMPtr.h" #include "nsIThread.h" #include "nsThreadUtils.h" +#include "nsITimer.h" template struct already_AddRefed; @@ -36,6 +37,10 @@ public: NS_DECL_MOZIGECKOMEDIAPLUGINSERVICE NS_DECL_NSIOBSERVER + void AsyncShutdownNeeded(GMPParent* aParent); + void AsyncShutdownComplete(GMPParent* aParent); + void AbortAsyncShutdown(); + private: ~GeckoMediaPluginService(); @@ -45,12 +50,16 @@ private: void UnloadPlugins(); void CrashPlugins(); + void SetAsyncShutdownComplete(); void LoadFromEnvironment(); void ProcessPossiblePlugin(nsIFile* aDir); void AddOnGMPThread(const nsAString& aSearchDir); void RemoveOnGMPThread(const nsAString& aSearchDir); + + nsresult SetAsyncShutdownTimeout(); + protected: friend class GMPParent; void ReAddOnGMPThread(nsRefPtr& aOld); @@ -80,6 +89,26 @@ private: nsCOMPtr mGMPThread; bool mShuttingDown; bool mShuttingDownOnGMPThread; + + template + class MainThreadOnly { + public: + MainThreadOnly(T aValue) + : mValue(aValue) + {} + operator T&() { + MOZ_ASSERT(NS_IsMainThread()); + return mValue; + } + + private: + T mValue; + }; + + MainThreadOnly mWaitingForPluginsAsyncShutdown; + + nsTArray> mAsyncShutdownPlugins; // GMP Thread only. + nsCOMPtr mAsyncShutdownTimeout; // GMP Thread only. }; } // namespace gmp diff --git a/content/media/gmp/PGMP.ipdl b/content/media/gmp/PGMP.ipdl index 86dfe0bf2ca9..9daa4784a347 100644 --- a/content/media/gmp/PGMP.ipdl +++ b/content/media/gmp/PGMP.ipdl @@ -28,12 +28,16 @@ parent: async PCrashReporter(NativeThreadId tid); async PGMPTimer(); + async AsyncShutdownComplete(); + async AsyncShutdownRequired(); + child: async PGMPAudioDecoder(); async PGMPDecryptor(); async PGMPVideoDecoder(); async PGMPVideoEncoder(); + async BeginAsyncShutdown(); async CrashPluginNow(); }; diff --git a/content/media/gmp/PGMPTimer.ipdl b/content/media/gmp/PGMPTimer.ipdl index fdcf694c0c75..57bea28f2c0e 100644 --- a/content/media/gmp/PGMPTimer.ipdl +++ b/content/media/gmp/PGMPTimer.ipdl @@ -1,22 +1,22 @@ -/* -*- 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 protocol PGMP; - -namespace mozilla { -namespace gmp { - -async protocol PGMPTimer -{ - manager PGMP; -child: - TimerExpired(uint32_t aTimerId); -parent: - SetTimer(uint32_t aTimerId, uint32_t aTimeoutMs); - __delete__(); -}; - -} // namespace gmp -} // namespace mozilla +/* -*- 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 protocol PGMP; + +namespace mozilla { +namespace gmp { + +async protocol PGMPTimer +{ + manager PGMP; +child: + TimerExpired(uint32_t aTimerId); +parent: + SetTimer(uint32_t aTimerId, uint32_t aTimeoutMs); + __delete__(); +}; + +} // namespace gmp +} // namespace mozilla