Bug 1312540 - Maintain a cache of GMPs capabilities in content processes. r=gerald,r=billm
In order to avoid doing a synchronous call from content process to chrome
process in order to determine what GMPs are usable, maintain a cache of GMP
capabilities in the content processes.
We must seed the cache when content processes are created, as the GMP service
is started up and GMPs are added to it before the first (or any subsequent)
content process is created.
MozReview-Commit-ID: Eb4Pu81XHmn
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -56,16 +56,17 @@
#include "mozilla/plugins/PluginInstanceParent.h"
#include "mozilla/plugins/PluginModuleParent.h"
#include "mozilla/widget/WidgetMessageUtils.h"
#include "nsBaseDragService.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/WebBrowserPersistDocumentChild.h"
#include "imgLoader.h"
+#include "GMPServiceChild.h"
#if defined(MOZ_CONTENT_SANDBOX)
#if defined(XP_WIN)
#define TARGET_SANDBOX_EXPORTS
#include "mozilla/sandboxTarget.h"
#elif defined(XP_LINUX)
#include "mozilla/Sandbox.h"
#include "mozilla/SandboxInfo.h"
@@ -1173,16 +1174,23 @@ ContentChild::AllocPContentBridgeParent(
PGMPServiceChild*
ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
return GMPServiceChild::Create(aTransport, aOtherProcess);
}
bool
+ContentChild::RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities)
+{
+ GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities));
+ return true;
+}
+
+bool
ContentChild::RecvInitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge)
{
if (!CompositorBridgeChild::InitForContent(Move(aCompositor))) {
return false;
}
if (!ImageBridgeChild::InitForContent(Move(aImageBridge))) {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -158,16 +158,19 @@ public:
AllocPContentBridgeChild(mozilla::ipc::Transport* transport,
base::ProcessId otherProcess) override;
PGMPServiceChild*
AllocPGMPServiceChild(mozilla::ipc::Transport* transport,
base::ProcessId otherProcess) override;
bool
+ RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities) override;
+
+ bool
RecvInitRendering(
Endpoint<PCompositorBridgeChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge) override;
bool
RecvReinitRendering(
Endpoint<PCompositorBridgeChild>&& aCompositor,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1410,16 +1410,19 @@ ContentParent::Init()
nsCOMPtr<nsISupports> gatherer;
rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
MOZ_ASSERT(NS_SUCCEEDED(rv));
mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
StartProfiler(currentProfilerParams);
}
#endif
+
+ RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton());
+ gmps->UpdateContentProcessGMPCapabilities();
}
void
ContentParent::ForwardKnownInfo()
{
MOZ_ASSERT(mMetamorphosed);
if (!mMetamorphosed) {
return;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -332,16 +332,29 @@ union GetFilesResponseResult
struct BlobURLRegistrationData
{
nsCString url;
PBlob blob;
Principal principal;
};
+struct GMPAPITags
+{
+ nsCString api;
+ nsCString[] tags;
+};
+
+struct GMPCapabilityData
+{
+ nsCString name;
+ nsCString version;
+ GMPAPITags[] capabilities;
+};
+
nested(upto inside_cpow) sync protocol PContent
{
parent spawns PPluginModule;
parent opens PProcessHangMonitor;
parent opens PGMPService;
child opens PBackground;
@@ -659,16 +672,19 @@ child:
async GetFilesResponse(nsID aID, GetFilesResponseResult aResult);
async BlobURLRegistration(nsCString aURI, PBlob aBlob,
Principal aPrincipal);
async BlobURLUnregistration(nsCString aURI);
+
+ async GMPsChanged(GMPCapabilityData[] capabilities);
+
parent:
/**
* Tell the content process some attributes of itself. This is
* among the first information queried by content processes after
* startup. (The message is sync to allow the content process to
* control when it receives the information.)
*
* |id| is a unique ID among all subprocesses. When |isForApp &&
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -155,16 +155,18 @@ public:
bool GetGMPContentParent(UniquePtr<GetGMPContentParentCallback>&& aCallback);
already_AddRefed<GMPContentParent> ForgetGMPContentParent();
bool EnsureProcessLoaded(base::ProcessId* aID);
bool Bridge(GMPServiceParent* aGMPServiceParent);
+ const nsTArray<GMPCapability>& GetCapabilities() const { return mCapabilities; }
+
private:
~GMPParent();
RefPtr<GeckoMediaPluginServiceParent> mService;
bool EnsureProcessLoaded();
RefPtr<GenericPromise> ReadGMPMetaData();
RefPtr<GenericPromise> ReadGMPInfoFile(nsIFile* aFile);
RefPtr<GenericPromise> ParseChromiumManifest(nsString aJSON); // Main thread.
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -1,15 +1,17 @@
/* -*- 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 "GMPServiceChild.h"
#include "mozilla/dom/ContentChild.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
#include "mozIGeckoMediaPluginService.h"
#include "mozIGeckoMediaPluginChromeService.h"
#include "nsCOMPtr.h"
#include "GMPParent.h"
#include "GMPContentParent.h"
#include "nsXPCOMPrivate.h"
#include "mozilla/SyncRunnable.h"
#include "runnable_utils.h"
@@ -123,16 +125,94 @@ GeckoMediaPluginServiceChild::GetContent
UniquePtr<GetServiceChildCallback> callback(
new GetContentParentFromDone(aHelper, aNodeId, aAPI, aTags, Move(aCallback)));
GetServiceChild(Move(callback));
return true;
}
+typedef mozilla::dom::GMPCapabilityData GMPCapabilityData;
+typedef mozilla::dom::GMPAPITags GMPAPITags;
+
+struct GMPCapabilityAndVersion
+{
+ explicit GMPCapabilityAndVersion(const GMPCapabilityData& aCapabilities)
+ : mName(aCapabilities.name())
+ , mVersion(aCapabilities.version())
+ {
+ for (const GMPAPITags& tags : aCapabilities.capabilities()) {
+ GMPCapability cap;
+ cap.mAPIName = tags.api();
+ for (const nsCString& tag : tags.tags()) {
+ cap.mAPITags.AppendElement(tag);
+ }
+ mCapabilities.AppendElement(Move(cap));
+ }
+ }
+
+ nsCString ToString() const
+ {
+ nsCString s;
+ s.Append(mName);
+ s.Append(" version=");
+ s.Append(mVersion);
+ s.Append(" tags=[");
+ nsCString tags;
+ for (const GMPCapability& cap : mCapabilities) {
+ if (!tags.IsEmpty()) {
+ tags.Append(" ");
+ }
+ tags.Append(cap.mAPIName);
+ for (const nsCString& tag : cap.mAPITags) {
+ tags.Append(":");
+ tags.Append(tag);
+ }
+ }
+ s.Append(tags);
+ s.Append("]");
+ return s;
+ }
+
+ nsCString mName;
+ nsCString mVersion;
+ nsTArray<GMPCapability> mCapabilities;
+};
+
+StaticAutoPtr<nsTArray<GMPCapabilityAndVersion>> sGMPCapabilities;
+
+static nsCString
+GMPCapabilitiesToString()
+{
+ nsCString s;
+ for (const GMPCapabilityAndVersion& gmp : *sGMPCapabilities) {
+ if (!s.IsEmpty()) {
+ s.Append(", ");
+ }
+ s.Append(gmp.ToString());
+ }
+ return s;
+}
+
+/* static */
+void
+GeckoMediaPluginServiceChild::UpdateGMPCapabilities(nsTArray<GMPCapabilityData>&& aCapabilities)
+{
+ if (!sGMPCapabilities) {
+ sGMPCapabilities = new nsTArray<GMPCapabilityAndVersion>();
+ ClearOnShutdown(&sGMPCapabilities);
+ }
+ sGMPCapabilities->Clear();
+ for (const GMPCapabilityData& plugin : aCapabilities) {
+ sGMPCapabilities->AppendElement(GMPCapabilityAndVersion(plugin));
+ }
+
+ LOGD(("UpdateGMPCapabilities {%s}", GMPCapabilitiesToString().get()));
+}
+
NS_IMETHODIMP
GeckoMediaPluginServiceChild::GetPluginVersionForAPI(const nsACString& aAPI,
nsTArray<nsCString>* aTags,
bool* aHasPlugin,
nsACString& aOutVersion)
{
dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
if (!contentChild) {
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -6,16 +6,17 @@
#ifndef GMPServiceChild_h_
#define GMPServiceChild_h_
#include "GMPService.h"
#include "base/process.h"
#include "mozilla/ipc/Transport.h"
#include "mozilla/gmp/PGMPServiceChild.h"
#include "nsRefPtrHashtable.h"
+#include "mozilla/dom/ContentChild.h"
namespace mozilla {
namespace gmp {
class GMPContentParent;
class GMPServiceChild;
class GetServiceChildCallback
@@ -50,16 +51,18 @@ public:
UniquePtr<GetNodeIdCallback>&& aCallback) override;
NS_DECL_NSIOBSERVER
void SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild);
void RemoveGMPContentParent(GMPContentParent* aGMPContentParent);
+ static void UpdateGMPCapabilities(nsTArray<mozilla::dom::GMPCapabilityData>&& aCapabilities);
+
protected:
void InitializePlugins(AbstractThread*) override
{
// Nothing to do here.
}
bool GetContentParentFrom(GMPCrashHelper* aHelper,
const nsACString& aNodeId,
const nsCString& aAPI,
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -3,16 +3,17 @@
* 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 "GMPServiceParent.h"
#include "GMPService.h"
#include "prio.h"
#include "base/task.h"
#include "mozilla/Logging.h"
+#include "mozilla/dom/ContentParent.h"
#include "GMPParent.h"
#include "GMPVideoDecoderParent.h"
#include "nsAutoPtr.h"
#include "nsIObserverService.h"
#include "GeckoChildProcessHost.h"
#include "mozilla/Preferences.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/SyncRunnable.h"
@@ -829,51 +830,102 @@ private:
};
NS_IMETHODIMP
GeckoMediaPluginServiceParent::PathRunnable::Run()
{
mService->RemoveOnGMPThread(mPath,
mOperation == REMOVE_AND_DELETE_FROM_DISK,
mDefer);
+
+ mService->UpdateContentProcessGMPCapabilities();
+
#ifndef MOZ_WIDGET_GONK // Bug 1214967: disabled on B2G due to inscrutable test failures.
// For e10s, we must fire a notification so that all ContentParents notify
// their children to update the codecs that the GMPDecoderModule can use.
NS_DispatchToMainThread(new NotifyObserversTask("gmp-changed"), NS_DISPATCH_NORMAL);
// For non-e10s, and for decoding in the chrome process, must update GMP
// PDM's codecs list directly.
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
GMPDecoderModule::UpdateUsableCodecs();
}));
#endif
return NS_OK;
}
+void
+GeckoMediaPluginServiceParent::UpdateContentProcessGMPCapabilities()
+{
+ if (!NS_IsMainThread()) {
+ nsCOMPtr<nsIRunnable> task =
+ NewRunnableMethod(this, &GeckoMediaPluginServiceParent::UpdateContentProcessGMPCapabilities);
+ NS_DispatchToMainThread(task);
+ return;
+ }
+
+ typedef mozilla::dom::GMPCapabilityData GMPCapabilityData;
+ typedef mozilla::dom::GMPAPITags GMPAPITags;
+ typedef mozilla::dom::ContentParent ContentParent;
+
+ nsTArray<GMPCapabilityData> caps;
+ {
+ MutexAutoLock lock(mMutex);
+ for (const RefPtr<GMPParent>& gmp : mPlugins) {
+ // We have multiple instances of a GMPParent for a given GMP in the
+ // list, one per origin. So filter the list so that we don't include
+ // the same GMP's capabilities twice.
+ NS_ConvertUTF16toUTF8 name(gmp->GetPluginBaseName());
+ bool found = false;
+ for (const GMPCapabilityData& cap : caps) {
+ if (cap.name().Equals(name)) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ continue;
+ }
+ GMPCapabilityData x;
+ x.name() = name;
+ x.version() = gmp->GetVersion();
+ for (const GMPCapability& tag : gmp->GetCapabilities()) {
+ x.capabilities().AppendElement(GMPAPITags(tag.mAPIName, tag.mAPITags));
+ }
+ caps.AppendElement(Move(x));
+ }
+ }
+ for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
+ Unused << cp->SendGMPsChanged(caps);
+ }
+}
+
RefPtr<GenericPromise>
GeckoMediaPluginServiceParent::AsyncAddPluginDirectory(const nsAString& aDirectory)
{
RefPtr<AbstractThread> thread(GetAbstractGMPThread());
if (!thread) {
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
nsString dir(aDirectory);
+ RefPtr<GeckoMediaPluginServiceParent> self = this;
return InvokeAsync(thread, this, __func__, &GeckoMediaPluginServiceParent::AddOnGMPThread, dir)
->Then(AbstractThread::MainThread(), __func__,
- [dir]() -> void {
+ [dir, self]() -> void {
LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s succeeded",
NS_ConvertUTF16toUTF8(dir).get()));
MOZ_ASSERT(NS_IsMainThread());
// For e10s, we must fire a notification so that all ContentParents notify
// their children to update the codecs that the GMPDecoderModule can use.
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
MOZ_ASSERT(obsService);
if (obsService) {
obsService->NotifyObservers(nullptr, "gmp-changed", nullptr);
}
+ self->UpdateContentProcessGMPCapabilities();
// For non-e10s, and for decoding in the chrome process, must update GMP
// PDM's codecs list directly.
GMPDecoderModule::UpdateUsableCodecs();
},
[dir]() -> void {
LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s failed",
NS_ConvertUTF16toUTF8(dir).get()));
})
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -67,16 +67,18 @@ public:
already_AddRefed<GMPStorage> GetMemoryStorageFor(const nsACString& aNodeId);
nsresult ForgetThisSiteNative(const nsAString& aSite,
const mozilla::OriginAttributesPattern& aPattern);
// Notifies that some user of this class is created/destroyed.
void ServiceUserCreated();
void ServiceUserDestroyed();
+ void UpdateContentProcessGMPCapabilities();
+
private:
friend class GMPServiceParent;
virtual ~GeckoMediaPluginServiceParent();
void ClearStorage();
already_AddRefed<GMPParent> SelectPluginForAPI(const nsACString& aNodeId,