mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1067216 - Make MediaKeys.isTypeSupported() more accurate. r=edwin,jesup
This commit is contained in:
parent
5ac2fe1151
commit
d0653badc3
@ -16,9 +16,16 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsContentTypeParser.h"
|
||||
#ifdef MOZ_FMP4
|
||||
#include "MP4Decoder.h"
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#endif
|
||||
#include "nsContentCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -107,15 +114,86 @@ MediaKeys::SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aCert, Error
|
||||
}
|
||||
|
||||
static bool
|
||||
IsSupportedKeySystem(const nsAString& aKeySystem)
|
||||
HaveGMPFor(const nsCString& aKeySystem,
|
||||
const nsCString& aAPI,
|
||||
const nsCString& aTag = EmptyCString())
|
||||
{
|
||||
return aKeySystem.EqualsASCII("org.w3.clearkey") ||
|
||||
#ifdef XP_WIN
|
||||
(aKeySystem.EqualsASCII("com.adobe.access") &&
|
||||
IsVistaOrLater() &&
|
||||
Preferences::GetBool("media.eme.adobe-access.enabled", false)) ||
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps =
|
||||
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
if (NS_WARN_IF(!mps)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> tags;
|
||||
tags.AppendElement(aKeySystem);
|
||||
if (!aTag.IsEmpty()) {
|
||||
tags.AppendElement(aTag);
|
||||
}
|
||||
// Note: EME plugins need a non-null nodeId here, as they must
|
||||
// not be shared across origins.
|
||||
bool hasPlugin = false;
|
||||
if (NS_FAILED(mps->HasPluginForAPI(aAPI,
|
||||
&tags,
|
||||
&hasPlugin))) {
|
||||
return false;
|
||||
}
|
||||
return hasPlugin;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsPlayableMP4Type(const nsAString& aContentType)
|
||||
{
|
||||
#ifdef MOZ_FMP4
|
||||
nsContentTypeParser parser(aContentType);
|
||||
nsAutoString mimeType;
|
||||
nsresult rv = parser.GetType(mimeType);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
nsAutoString codecs;
|
||||
parser.GetParameter("codecs", codecs);
|
||||
|
||||
NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
|
||||
return MP4Decoder::CanHandleMediaType(mimeTypeUTF8, codecs);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
false;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaKeys::IsTypeSupported(const nsAString& aKeySystem,
|
||||
const Optional<nsAString>& aInitDataType,
|
||||
const Optional<nsAString>& aContentType)
|
||||
{
|
||||
if (aKeySystem.EqualsLiteral("org.w3.clearkey") &&
|
||||
(!aInitDataType.WasPassed() || aInitDataType.Value().EqualsLiteral("cenc")) &&
|
||||
(!aContentType.WasPassed() || IsPlayableMP4Type(aContentType.Value())) &&
|
||||
HaveGMPFor(NS_LITERAL_CSTRING("org.w3.clearkey"),
|
||||
NS_LITERAL_CSTRING("eme-decrypt"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Note: Adobe Access's GMP uses WMF to decode, so anything our MP4Reader
|
||||
// thinks it can play on Windows, the Access GMP should be able to play.
|
||||
if (aKeySystem.EqualsLiteral("com.adobe.access") &&
|
||||
Preferences::GetBool("media.eme.adobe-access.enabled", false) &&
|
||||
IsVistaOrLater() && // Win Vista and later only.
|
||||
(!aInitDataType.WasPassed() || aInitDataType.Value().EqualsLiteral("cenc")) &&
|
||||
(!aContentType.WasPassed() || IsPlayableMP4Type(aContentType.Value())) &&
|
||||
HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
|
||||
NS_LITERAL_CSTRING("eme-decrypt")) &&
|
||||
HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
|
||||
NS_LITERAL_CSTRING("decode-video"),
|
||||
NS_LITERAL_CSTRING("h264")) &&
|
||||
HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
|
||||
NS_LITERAL_CSTRING("decode-audio"),
|
||||
NS_LITERAL_CSTRING("aac"))) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -128,8 +206,16 @@ MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
|
||||
{
|
||||
// TODO: Should really get spec changed to this is async, so we can wait
|
||||
// for user to consent to running plugin.
|
||||
return IsSupportedKeySystem(aKeySystem) ? IsTypeSupportedResult::Maybe
|
||||
: IsTypeSupportedResult::_empty;
|
||||
bool supported = IsTypeSupported(aKeySystem, aInitDataType, aContentType);
|
||||
|
||||
EME_LOG("MediaKeys::IsTypeSupported keySystem='%s' initDataType='%s' contentType='%s' supported=%d",
|
||||
NS_ConvertUTF16toUTF8(aKeySystem).get(),
|
||||
(aInitDataType.WasPassed() ? NS_ConvertUTF16toUTF8(aInitDataType.Value()).get() : ""),
|
||||
(aContentType.WasPassed() ? NS_ConvertUTF16toUTF8(aContentType.Value()).get() : ""),
|
||||
supported);
|
||||
|
||||
return supported ? IsTypeSupportedResult::Probably
|
||||
: IsTypeSupportedResult::_empty;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
@ -242,7 +328,7 @@ MediaKeys::Init(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!IsSupportedKeySystem(mKeySystem)) {
|
||||
if (!IsTypeSupported(mKeySystem)) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
@ -271,7 +357,7 @@ MediaKeys::Init(ErrorResult& aRv)
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
|
||||
mTopLevelPrincipal = top->GetExtantDoc()->NodePrincipal();
|
||||
|
||||
if (!mPrincipal || !mTopLevelPrincipal) {
|
||||
|
@ -124,6 +124,10 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
static bool IsTypeSupported(const nsAString& aKeySystem,
|
||||
const Optional<nsAString>& aInitDataType = Optional<nsAString>(),
|
||||
const Optional<nsAString>& aContentType = Optional<nsAString>());
|
||||
|
||||
bool IsInPrivateBrowsing();
|
||||
already_AddRefed<Promise> Init(ErrorResult& aRv);
|
||||
|
||||
|
@ -143,6 +143,7 @@ GeckoMediaPluginService::GeckoMediaPluginService()
|
||||
: mMutex("GeckoMediaPluginService::mMutex")
|
||||
, mShuttingDown(false)
|
||||
, mShuttingDownOnGMPThread(false)
|
||||
, mScannedPluginOnDisk(false)
|
||||
, mWaitingForPluginsAsyncShutdown(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@ -590,7 +591,7 @@ GeckoMediaPluginService::UnloadPlugins()
|
||||
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++) {
|
||||
for (size_t i = 0; i < mPlugins.Length(); i++) {
|
||||
mPlugins[i]->CloseActive(true);
|
||||
}
|
||||
mPlugins.Clear();
|
||||
@ -619,7 +620,7 @@ GeckoMediaPluginService::CrashPlugins()
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
for (uint32_t i = 0; i < mPlugins.Length(); i++) {
|
||||
for (size_t i = 0; i < mPlugins.Length(); i++) {
|
||||
mPlugins[i]->Crash();
|
||||
}
|
||||
}
|
||||
@ -652,6 +653,8 @@ GeckoMediaPluginService::LoadFromEnvironment()
|
||||
pos = next + 1;
|
||||
}
|
||||
}
|
||||
|
||||
mScannedPluginOnDisk = true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -693,47 +696,88 @@ GeckoMediaPluginService::RemovePluginDirectory(const nsAString& aDirectory)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class DummyRunnable : public nsRunnable {
|
||||
public:
|
||||
NS_IMETHOD Run() { return NS_OK; }
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginService::HasPluginForAPI(const nsACString& aNodeId,
|
||||
const nsACString& aAPI,
|
||||
GeckoMediaPluginService::HasPluginForAPI(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(aNodeId, temp, *aTags, false);
|
||||
*aResult = !!parent;
|
||||
const char* env = nullptr;
|
||||
if (!mScannedPluginOnDisk && (env = PR_GetEnv("MOZ_GMP_PATH")) && *env) {
|
||||
// We have a MOZ_GMP_PATH environment variable which may specify the
|
||||
// location of plugins to load, and we haven't yet scanned the disk to
|
||||
// see if there are plugins there. Get the GMP thread, which will
|
||||
// cause an event to be dispatched to which scans for plugins. We
|
||||
// dispatch a sync event to the GMP thread here in order to wait until
|
||||
// after the GMP thread has scanned any paths in MOZ_GMP_PATH.
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsresult rv = GetThread(getter_AddRefs(thread));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
thread->Dispatch(new DummyRunnable(), NS_DISPATCH_SYNC);
|
||||
MOZ_ASSERT(mScannedPluginOnDisk, "Should have scanned MOZ_GMP_PATH by now");
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
nsCString api(aAPI);
|
||||
GMPParent* gmp = FindPluginForAPIFrom(0, api, *aTags, nullptr);
|
||||
*aResult = (gmp != nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GMPParent*
|
||||
GeckoMediaPluginService::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];
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
GMPParent*
|
||||
GeckoMediaPluginService::SelectPluginForAPI(const nsACString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags,
|
||||
bool aCloneCrossNodeIds)
|
||||
const nsTArray<nsCString>& aTags)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread || !aCloneCrossNodeIds,
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread,
|
||||
"Can't clone GMP plugins on non-GMP threads.");
|
||||
|
||||
GMPParent* gmpToClone = nullptr;
|
||||
{
|
||||
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;
|
||||
}
|
||||
size_t index = 0;
|
||||
GMPParent* gmp = nullptr;
|
||||
while ((gmp = FindPluginForAPIFrom(index, aAPI, aTags, &index))) {
|
||||
if (aNodeId.IsEmpty()) {
|
||||
if (gmp->CanBeSharedCrossNodeIds()) {
|
||||
return gmp;
|
||||
@ -744,15 +788,17 @@ GeckoMediaPluginService::SelectPluginForAPI(const nsACString& aNodeId,
|
||||
return gmp;
|
||||
}
|
||||
|
||||
// This GMP has the correct type but has the wrong origin; hold on to it
|
||||
// This GMP has the correct type but has the wrong nodeId; hold on to it
|
||||
// in case we need to clone it.
|
||||
gmpToClone = gmp;
|
||||
// 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 (aCloneCrossNodeIds && gmpToClone) {
|
||||
if (gmpToClone) {
|
||||
GMPParent* clone = ClonePlugin(gmpToClone);
|
||||
if (!aNodeId.IsEmpty()) {
|
||||
clone->SetNodeId(aNodeId);
|
||||
@ -847,7 +893,7 @@ GeckoMediaPluginService::RemoveOnGMPThread(const nsAString& aDirectory)
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
|
||||
for (size_t i = 0; i < mPlugins.Length(); ++i) {
|
||||
nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory();
|
||||
bool equals;
|
||||
if (NS_SUCCEEDED(directory->Equals(pluginpath, &equals)) && equals) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "nsITimer.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
template <class> struct already_AddRefed;
|
||||
|
||||
@ -49,8 +50,11 @@ private:
|
||||
|
||||
GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags,
|
||||
bool aCloneCrossNodeIds = true);
|
||||
const nsTArray<nsCString>& aTags);
|
||||
GMPParent* FindPluginForAPIFrom(size_t aSearchStartIndex,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags,
|
||||
size_t* aOutPluginIndex);
|
||||
|
||||
void UnloadPlugins();
|
||||
void CrashPlugins();
|
||||
@ -94,6 +98,10 @@ private:
|
||||
bool mShuttingDown;
|
||||
bool mShuttingDownOnGMPThread;
|
||||
|
||||
// True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
|
||||
// plugins found there into mPlugins.
|
||||
Atomic<bool> mScannedPluginOnDisk;
|
||||
|
||||
template<typename T>
|
||||
class MainThreadOnly {
|
||||
public:
|
||||
|
@ -26,7 +26,7 @@ class GMPVideoHost;
|
||||
[ptr] native GMPDecryptorProxy(GMPDecryptorProxy);
|
||||
[ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy);
|
||||
|
||||
[scriptable, uuid(b350d3b6-00c9-4602-bdfe-84c2be8d1e7a)]
|
||||
[scriptable, uuid(657443a4-6b5d-4181-98b0-56997b35bd57)]
|
||||
interface mozIGeckoMediaPluginService : nsISupports
|
||||
{
|
||||
|
||||
@ -40,9 +40,7 @@ interface mozIGeckoMediaPluginService : nsISupports
|
||||
* Callable on any thread
|
||||
*/
|
||||
[noscript]
|
||||
boolean hasPluginForAPI([optional] in ACString nodeId,
|
||||
in ACString api,
|
||||
in TagArray tags);
|
||||
boolean hasPluginForAPI(in ACString api, in TagArray tags);
|
||||
|
||||
/**
|
||||
* Get a video decoder that supports the specified tags.
|
||||
|
@ -174,6 +174,9 @@ function startTest(test, token)
|
||||
}, bail("failed to create MediaKeys object")).then(function() {
|
||||
info(token + " set MediaKeys on <video> element ok");
|
||||
|
||||
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
|
||||
"MediaKeys should still support keysystem after CDM created...");
|
||||
|
||||
var session = v.mediaKeys.createSession(test.sessionType);
|
||||
session.addEventListener("message", UpdateSessionFunc(test));
|
||||
session.generateRequest(ev.initDataType, ev.initData).then(function() {
|
||||
@ -202,7 +205,23 @@ function startTest(test, token)
|
||||
PlayTest(test, v);
|
||||
}
|
||||
|
||||
function testIsTypeSupported()
|
||||
{
|
||||
var t = MediaKeys.isTypeSupported;
|
||||
const clearkey = "org.w3.clearkey";
|
||||
ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
|
||||
ok(t(clearkey), "ClearKey supported.");
|
||||
ok(!t(clearkey, "bogus"), "ClearKey bogus initDataType not supported.");
|
||||
ok(t(clearkey, "cenc"), "ClearKey/cenc should be supported.");
|
||||
ok(!t(clearkey, "cenc", "bogus"), "ClearKey/cenc bogus content type should be supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4'), "ClearKey/cenc video/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'video/mp4; codecs="avc1.4d4015,mp4a.40.2"'), "ClearKey/cenc H.264/AAC supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4'), "ClearKey/cenc audio/mp4 supported.");
|
||||
ok(t(clearkey, "cenc", 'audio/mp4; codecs="mp4a.40.2"'), "ClearKey/cenc AAC LC supported.");
|
||||
}
|
||||
|
||||
function beginTest() {
|
||||
testIsTypeSupported();
|
||||
manager.runTests(gEMETests, startTest);
|
||||
}
|
||||
|
||||
|
@ -203,16 +203,14 @@ int VcmSIPCCBinding::getVideoCodecsGmp()
|
||||
// H.264 only for now
|
||||
bool has_gmp;
|
||||
nsresult rv;
|
||||
rv = gSelf->mGMPService->HasPluginForAPI(NS_LITERAL_CSTRING(""),
|
||||
NS_LITERAL_CSTRING("encode-video"),
|
||||
rv = gSelf->mGMPService->HasPluginForAPI(NS_LITERAL_CSTRING("encode-video"),
|
||||
&tags,
|
||||
&has_gmp);
|
||||
if (NS_FAILED(rv) || !has_gmp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rv = gSelf->mGMPService->HasPluginForAPI(NS_LITERAL_CSTRING(""),
|
||||
NS_LITERAL_CSTRING("decode-video"),
|
||||
rv = gSelf->mGMPService->HasPluginForAPI(NS_LITERAL_CSTRING("decode-video"),
|
||||
&tags,
|
||||
&has_gmp);
|
||||
if (NS_FAILED(rv) || !has_gmp) {
|
||||
|
Loading…
Reference in New Issue
Block a user