Bug 1268984 - Ensure GMPs are re-inserted in GMPServiceParent::mPlugins in the same order in ReAddOnGMPThread. r=jesup
The GMP which GeckoMediaPluginServiceParent::FindPluginForAPIFrom() returns
depends on the order in which GMPs lie in GMPServiceParent::mPlugins. However
when we shutdown a GMPParent we remove and then re-append the GMPParent to
mPlugins. This means the order in which GMPs lie in the list changes.
So when WebRTC requests an H.264 decoder, the first time it will get OpenH264,
since that's first in the list. But once we dispose of that decoder, its
GMPParent will be cloned and the clone will be appended to the end of the
list. This means the next time WebRTC requests a decoder, it'll get whatever
was next in the list.
This could be the Adobe GMP, which seems to be able to handle whatever WebRTC
is putting into it. However, if you do this enough times, you'll get the
Widevine CDM, which can't handle whatever WebRTC is putting into it.
So a quick hack to fix this is in ReAddOnGMPThread is to re-insert the clone
of the GMP into the slot in mPlugins that the original occupied. Then WebRTC
will always get OpenH264 whenever it requests for an H.264 decoder, as the
order of the GMPParents in mPlugins won't change.
MozReview-Commit-ID: Ii4AMqDqAo9
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -905,17 +905,17 @@ GeckoMediaPluginServiceParent::GetPlugin
MutexAutoLock lock(mMutex);
nsCString api(aAPI);
size_t index = 0;
// We must parse the version number into a float for comparison. Yuck.
double maxParsedVersion = -1.;
*aHasPlugin = false;
- while (GMPParent* gmp = FindPluginForAPIFrom(index, api, *aTags, &index)) {
+ while (RefPtr<GMPParent> gmp = FindPluginForAPIFrom(index, api, *aTags, &index)) {
*aHasPlugin = true;
double parsedVersion = atof(gmp->GetVersion().get());
if (maxParsedVersion < 0 || parsedVersion > maxParsedVersion) {
maxParsedVersion = parsedVersion;
aOutVersion = gmp->GetVersion();
}
index++;
}
@@ -938,66 +938,66 @@ GeckoMediaPluginServiceParent::EnsurePlu
nsresult rv = GMPDispatch(new mozilla::Runnable(), NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(mScannedPluginOnDisk, "Should have scanned MOZ_GMP_PATH by now");
}
return NS_OK;
}
-GMPParent*
+already_AddRefed<GMPParent>
GeckoMediaPluginServiceParent::FindPluginForAPIFrom(size_t aSearchStartIndex,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags,
size_t* aOutPluginIndex)
{
mMutex.AssertCurrentThreadOwns();
for (size_t i = aSearchStartIndex; i < mPlugins.Length(); i++) {
- GMPParent* gmp = mPlugins[i];
+ RefPtr<GMPParent> gmp = mPlugins[i];
bool supportsAllTags = true;
for (size_t t = 0; t < aTags.Length(); t++) {
const nsCString& tag = aTags.ElementAt(t);
if (!gmp->SupportsAPI(aAPI, tag)) {
supportsAllTags = false;
break;
}
}
if (!supportsAllTags) {
continue;
}
if (aOutPluginIndex) {
*aOutPluginIndex = i;
}
- return gmp;
+ return gmp.forget();
}
return nullptr;
}
-GMPParent*
+already_AddRefed<GMPParent>
GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread,
"Can't clone GMP plugins on non-GMP threads.");
GMPParent* gmpToClone = nullptr;
{
MutexAutoLock lock(mMutex);
size_t index = 0;
- GMPParent* gmp = nullptr;
+ RefPtr<GMPParent> gmp;
while ((gmp = FindPluginForAPIFrom(index, aAPI, aTags, &index))) {
if (aNodeId.IsEmpty()) {
if (gmp->CanBeSharedCrossNodeIds()) {
- return gmp;
+ return gmp.forget();
}
} else if (gmp->CanBeUsedFrom(aNodeId)) {
MOZ_ASSERT(!aNodeId.IsEmpty());
gmp->SetNodeId(aNodeId);
- return gmp;
+ return gmp.forget();
}
if (!gmpToClone ||
(gmpToClone->IsMarkedForDeletion() && !gmp->IsMarkedForDeletion())) {
// This GMP has the correct type but has the wrong nodeId; hold on to it
// in case we need to clone it.
// Prefer GMPs in-use for the case where an upgraded plugin version is
// waiting for the old one to die. If the old plugin is in use, we
@@ -1009,21 +1009,25 @@ GeckoMediaPluginServiceParent::SelectPlu
// Loop around and try the next plugin; it may be usable from aNodeId.
index++;
}
}
// Plugin exists, but we can't use it due to cross-origin separation. Create a
// new one.
if (gmpToClone) {
- GMPParent* clone = ClonePlugin(gmpToClone);
+ RefPtr<GMPParent> clone = ClonePlugin(gmpToClone);
+ {
+ MutexAutoLock lock(mMutex);
+ mPlugins.AppendElement(clone);
+ }
if (!aNodeId.IsEmpty()) {
clone->SetNodeId(aNodeId);
}
- return clone;
+ return clone.forget();
}
return nullptr;
}
RefPtr<GMPParent>
CreateGMPParent()
{
@@ -1034,33 +1038,30 @@ CreateGMPParent()
return nullptr;
}
NS_WARNING("Loading media plugin despite lack of sandboxing.");
}
#endif
return new GMPParent();
}
-GMPParent*
+already_AddRefed<GMPParent>
GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
{
MOZ_ASSERT(aOriginal);
RefPtr<GMPParent> gmp = CreateGMPParent();
nsresult rv = gmp ? gmp->CloneFrom(aOriginal) : NS_ERROR_NOT_AVAILABLE;
if (NS_FAILED(rv)) {
NS_WARNING("Can't Create GMPParent");
return nullptr;
}
- MutexAutoLock lock(mMutex);
- mPlugins.AppendElement(gmp);
-
- return gmp.get();
+ return gmp.forget();
}
RefPtr<GenericPromise>
GeckoMediaPluginServiceParent::AddOnGMPThread(nsString aDirectory)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
@@ -1193,25 +1194,31 @@ GeckoMediaPluginServiceParent::PluginTer
void
GeckoMediaPluginServiceParent::ReAddOnGMPThread(const RefPtr<GMPParent>& aOld)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, (void*) aOld));
RefPtr<GMPParent> gmp;
if (!mShuttingDownOnGMPThread) {
- // Don't re-add plugin if we're shutting down. Let the old plugin die.
+ // We're not shutting down, so replace the old plugin in the list with a
+ // clone which is in a pristine state. Note: We place the plugin in
+ // the same slot in the array as a hack to ensure if we re-request with
+ // the same capabilities we get an instance of the same plugin.
gmp = ClonePlugin(aOld);
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(mPlugins.Contains(aOld));
+ if (mPlugins.Contains(aOld)) {
+ mPlugins[mPlugins.IndexOf(aOld)] = gmp;
+ }
+ } else {
+ // We're shutting down; don't re-add plugin, let the old plugin die.
+ MutexAutoLock lock(mMutex);
+ mPlugins.RemoveElement(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));
}
NS_IMETHODIMP
GeckoMediaPluginServiceParent::GetStorageDir(nsIFile** aOutFile)
{
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -64,23 +64,24 @@ public:
private:
friend class GMPServiceParent;
virtual ~GeckoMediaPluginServiceParent();
void ClearStorage();
- GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
- const nsCString& aAPI,
- const nsTArray<nsCString>& aTags);
- GMPParent* FindPluginForAPIFrom(size_t aSearchStartIndex,
- const nsCString& aAPI,
- const nsTArray<nsCString>& aTags,
- size_t* aOutPluginIndex);
+ already_AddRefed<GMPParent> SelectPluginForAPI(const nsACString& aNodeId,
+ const nsCString& aAPI,
+ const nsTArray<nsCString>& aTags);
+
+ already_AddRefed<GMPParent> FindPluginForAPIFrom(size_t aSearchStartIndex,
+ const nsCString& aAPI,
+ const nsTArray<nsCString>& aTags,
+ size_t* aOutPluginIndex);
nsresult GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
const nsAString& aGMPName,
bool aInPrivateBrowsing, nsACString& aOutId);
void UnloadPlugins();
void CrashPlugins();
void NotifySyncShutdownComplete();
@@ -112,17 +113,19 @@ protected:
RefPtr<GenericPromise::AllPromiseType> LoadFromEnvironment();
RefPtr<GenericPromise> AddOnGMPThread(nsString aDirectory);
bool GetContentParentFrom(const nsACString& aNodeId,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags,
UniquePtr<GetGMPContentParentCallback>&& aCallback)
override;
private:
- GMPParent* ClonePlugin(const GMPParent* aOriginal);
+ // Creates a copy of aOriginal. Note that the caller is responsible for
+ // adding this to GeckoMediaPluginServiceParent::mPlugins.
+ already_AddRefed<GMPParent> ClonePlugin(const GMPParent* aOriginal);
nsresult EnsurePluginsOnDiskScanned();
nsresult InitStorage();
class PathRunnable : public Runnable
{
public:
enum EOperation {
REMOVE,