diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp
index 8c807a93cbd2..4494f400742e 100644
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -443,6 +443,9 @@ GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
for (const nsString& candidate : aCandidate.mInitDataTypes.Value()) {
if (candidate.EqualsLiteral("cenc")) {
initDataTypes.AppendElement(candidate);
+ } else if (candidate.EqualsLiteral("keyids") &&
+ aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+ initDataTypes.AppendElement(candidate);
}
}
if (initDataTypes.IsEmpty()) {
diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini
index 315178163e82..c58988e62483 100644
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -619,6 +619,8 @@ skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'go
[test_eme_session_callable_value.html]
[test_eme_canvas_blocked.html]
skip-if = toolkit == 'android' # bug 1149374
+[test_eme_key_ids_initdata.html]
+skip-if = toolkit == 'android' # bug 1149374
[test_eme_non_mse_fails.html]
skip-if = toolkit == 'android' # bug 1149374
[test_eme_request_notifications.html]
diff --git a/dom/media/test/test_eme_key_ids_initdata.html b/dom/media/test/test_eme_key_ids_initdata.html
new file mode 100644
index 000000000000..ba688648d9de
--- /dev/null
+++ b/dom/media/test/test_eme_key_ids_initdata.html
@@ -0,0 +1,111 @@
+
+
+
+ Test Encrypted Media Extensions
+
+
+
+
+
+
+
+
+
+
+
diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
index 93df08a0c15b..2e14d822ee46 100644
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -113,12 +113,14 @@ public:
CreateSessionTask(ClearKeySessionManager* aTarget,
uint32_t aCreateSessionToken,
uint32_t aPromiseId,
+ const string& aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType)
: mTarget(aTarget)
, mCreateSessionToken(aCreateSessionToken)
, mPromiseId(aPromiseId)
+ , mInitDataType(aInitDataType)
, mSessionType(aSessionType)
{
mInitData.insert(mInitData.end(),
@@ -128,8 +130,8 @@ public:
virtual void Run() override {
mTarget->CreateSession(mCreateSessionToken,
mPromiseId,
- "cenc",
- strlen("cenc"),
+ mInitDataType.c_str(),
+ mInitDataType.size(),
&mInitData.front(),
mInitData.size(),
mSessionType);
@@ -141,6 +143,7 @@ private:
RefPtr mTarget;
uint32_t mCreateSessionToken;
uint32_t mPromiseId;
+ const string mInitDataType;
vector mInitData;
GMPSessionType mSessionType;
};
@@ -150,6 +153,7 @@ private:
ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
uint32_t aCreateSessionToken,
uint32_t aPromiseId,
+ const string& aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType)
@@ -160,6 +164,7 @@ ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInsta
GMPTask* t = new CreateSessionTask(aInstance,
aCreateSessionToken,
aPromiseId,
+ aInitDataType,
aInitData,
aInitDataSize,
aSessionType);
diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.h b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
index 3cbb3513ce93..ddab9f783c1d 100644
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -31,6 +31,7 @@ public:
static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
uint32_t aCreateSessionToken,
uint32_t aPromiseId,
+ const std::string& aInitDataType,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType);
diff --git a/media/gmp-clearkey/0.1/ClearKeySession.cpp b/media/gmp-clearkey/0.1/ClearKeySession.cpp
index e414ce110e93..8dcad9afa1f1 100644
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -54,13 +54,25 @@ ClearKeySession::~ClearKeySession()
void
ClearKeySession::Init(uint32_t aCreateSessionToken,
uint32_t aPromiseId,
+ const std::string& aInitDataType,
const uint8_t* aInitData, uint32_t aInitDataSize)
{
CK_LOGD("ClearKeySession::Init");
- ClearKeyUtils::ParseInitData(aInitData, aInitDataSize, mKeyIds);
+ if (aInitDataType == "cenc") {
+ ClearKeyUtils::ParseCENCInitData(aInitData, aInitDataSize, mKeyIds);
+ } else if (aInitDataType == "keyids") {
+ std::string sessionType;
+ ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds, sessionType);
+ if (sessionType != ClearKeyUtils::SessionTypeToString(mSessionType)) {
+ const char message[] = "Session type specified in keyids init data doesn't match session type.";
+ mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message));
+ return;
+ }
+ }
+
if (!mKeyIds.size()) {
- const char message[] = "Couldn't parse cenc key init data";
+ const char message[] = "Couldn't parse init data";
mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message));
return;
}
diff --git a/media/gmp-clearkey/0.1/ClearKeySession.h b/media/gmp-clearkey/0.1/ClearKeySession.h
index 14886d3227b8..2783b2253b19 100644
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -38,6 +38,7 @@ public:
void Init(uint32_t aCreateSessionToken,
uint32_t aPromiseId,
+ const string& aInitDataType,
const uint8_t* aInitData, uint32_t aInitDataSize);
GMPSessionType Type() const;
diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
index 17cb8ced1ba1..6a089c265856 100644
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -101,8 +101,9 @@ ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
{
CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType);
- // initDataType must be "cenc".
- if (strcmp("cenc", aInitDataType)) {
+ string initDataType(aInitDataType, aInitDataType + aInitDataTypeSize);
+ // initDataType must be "cenc" or "keyids".
+ if (initDataType != "cenc" && initDataType != "keyids") {
mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
nullptr /* message */, 0 /* messageLen */);
return;
@@ -111,6 +112,7 @@ ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
aCreateSessionToken,
aPromiseId,
+ initDataType,
aInitData,
aInitDataSize,
aSessionType)) {
@@ -121,7 +123,7 @@ ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
assert(mSessions.find(sessionId) == mSessions.end());
ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
- session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize);
+ session->Init(aCreateSessionToken, aPromiseId, initDataType, aInitData, aInitDataSize);
mSessions[sessionId] = session;
const vector& sessionKeys = session->GetKeyIds();
diff --git a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
index b98674335b0c..ef4697699e2e 100644
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
@@ -132,8 +132,9 @@ EncodeBase64Web(vector aBinary, string& aEncoded)
}
/* static */ void
-ClearKeyUtils::ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize,
- vector& aOutKeys)
+ClearKeyUtils::ParseCENCInitData(const uint8_t* aInitData,
+ uint32_t aInitDataSize,
+ vector& aOutKeyIds)
{
using mozilla::BigEndian;
@@ -182,7 +183,7 @@ ClearKeyUtils::ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize,
}
for (uint32_t i = 0; i < kidCount; i++) {
- aOutKeys.push_back(KeyId(data, data + CLEARKEY_KEY_LEN));
+ aOutKeyIds.push_back(KeyId(data, data + CLEARKEY_KEY_LEN));
data += CLEARKEY_KEY_LEN;
}
}
@@ -195,7 +196,7 @@ ClearKeyUtils::MakeKeyRequest(const vector& aKeyIDs,
{
assert(aKeyIDs.size() && aOutRequest.empty());
- aOutRequest.append("{ \"kids\":[");
+ aOutRequest.append("{\"kids\":[");
for (size_t i = 0; i < aKeyIDs.size(); i++) {
if (i) {
aOutRequest.append(",");
@@ -208,7 +209,7 @@ ClearKeyUtils::MakeKeyRequest(const vector& aKeyIDs,
aOutRequest.append("\"");
}
- aOutRequest.append("], \"type\":");
+ aOutRequest.append("],\"type\":");
aOutRequest.append("\"");
aOutRequest.append(SessionTypeToString(aSessionType));
@@ -508,6 +509,80 @@ ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
return true;
}
+static bool
+ParseKeyIds(ParserContext& aCtx, vector& aOutKeyIds)
+{
+ // Consume start of array.
+ EXPECT_SYMBOL(aCtx, '[');
+
+ while (true) {
+ string label;
+ vector keyId;
+ if (!GetNextLabel(aCtx, label) ||
+ !DecodeBase64KeyOrId(label, keyId)) {
+ return false;
+ }
+ assert(!keyId.empty());
+ aOutKeyIds.push_back(keyId);
+
+ uint8_t sym = PeekSymbol(aCtx);
+ if (!sym || sym == ']') {
+ break;
+ }
+
+ EXPECT_SYMBOL(aCtx, ',');
+ }
+
+ return GetNextSymbol(aCtx) == ']';
+}
+
+
+/* static */ bool
+ClearKeyUtils::ParseKeyIdsInitData(const uint8_t* aInitData,
+ uint32_t aInitDataSize,
+ vector& aOutKeyIds,
+ string& aOutSessionType)
+{
+ aOutSessionType = "temporary";
+
+ ParserContext ctx;
+ ctx.mIter = aInitData;
+ ctx.mEnd = aInitData + aInitDataSize;
+
+ // Consume '{' from start of object.
+ EXPECT_SYMBOL(ctx, '{');
+
+ while (true) {
+ string label;
+ // Consume member kids.
+ if (!GetNextLabel(ctx, label)) return false;
+ EXPECT_SYMBOL(ctx, ':');
+
+ if (label == "kids") {
+ // Parse "kids" array.
+ if (!ParseKeyIds(ctx, aOutKeyIds)) return false;
+ } else if (label == "type") {
+ // Consume type string.
+ if (!GetNextLabel(ctx, aOutSessionType)) return false;
+ } else {
+ SkipToken(ctx);
+ }
+
+ // Check for end of object.
+ if (PeekSymbol(ctx) == '}') {
+ break;
+ }
+
+ // Consume ',' between object members.
+ EXPECT_SYMBOL(ctx, ',');
+ }
+
+ // Consume '}' from end of object.
+ EXPECT_SYMBOL(ctx, '}');
+
+ return true;
+}
+
/* static */ const char*
ClearKeyUtils::SessionTypeToString(GMPSessionType aSessionType)
{
diff --git a/media/gmp-clearkey/0.1/ClearKeyUtils.h b/media/gmp-clearkey/0.1/ClearKeyUtils.h
index 6b70c6cebbcc..fc10b20c7cb9 100644
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.h
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.h
@@ -54,8 +54,14 @@ public:
static void DecryptAES(const std::vector& aKey,
std::vector& aData, std::vector& aIV);
- static void ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize,
- std::vector& aOutKeys);
+ static void ParseCENCInitData(const uint8_t* aInitData,
+ uint32_t aInitDataSize,
+ std::vector& aOutKeyIds);
+
+ static bool ParseKeyIdsInitData(const uint8_t* aInitData,
+ uint32_t aInitDataSize,
+ std::vector& aOutKeyIds,
+ std::string& aOutSessionType);
static void MakeKeyRequest(const std::vector& aKeyIds,
std::string& aOutRequest,