Bug 1075199 - Separate session management from ClearKeyDecryptionManager - r=cpearce
authorEdwin Flores <edwin@mozilla.com>
Mon, 12 Jan 2015 20:15:55 +1300
changeset 249142 b096ec537e81ffa5a936761eb6f0e1d317fef0e3
parent 249141 0fe67aa7e9aca39dd6bfe0bd23a752647b96061a
child 249143 c86c43915254b769d8b63cdf997a159d32905055
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1075199
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1075199 - Separate session management from ClearKeyDecryptionManager - r=cpearce
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
media/gmp-clearkey/0.1/ClearKeyPersistence.h
media/gmp-clearkey/0.1/ClearKeySession.h
media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
media/gmp-clearkey/0.1/ClearKeySessionManager.h
media/gmp-clearkey/0.1/RefCounted.h
media/gmp-clearkey/0.1/gmp-clearkey.cpp
media/gmp-clearkey/0.1/moz.build
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -1,555 +1,204 @@
 /* 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 <stdint.h>
-#include <stdio.h>
+#include <string.h>
+#include <vector>
 
 #include "ClearKeyDecryptionManager.h"
-#include "ClearKeyUtils.h"
-#include "ClearKeyStorage.h"
-#include "ClearKeyPersistence.h"
-#include "gmp-task-utils.h"
-
+#include "gmp-decryption.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 
-using namespace mozilla;
-using namespace std;
-
-class ClearKeyDecryptor
+class ClearKeyDecryptor : public RefCounted
 {
 public:
-  ClearKeyDecryptor(GMPDecryptorCallback* aCallback, const Key& aKey);
-  ~ClearKeyDecryptor();
-
-  void InitKey();
+  MOZ_IMPLICIT ClearKeyDecryptor();
 
-  void QueueDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
+  void InitKey(const Key& aKey);
+  bool HasKey() const { return !!mKey.size(); }
 
-  uint32_t AddRef();
-  uint32_t Release();
+  GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
+                 GMPEncryptedBufferMetadata* aMetadata);
 
   const Key& DecryptionKey() const { return mKey; }
 
 private:
-  struct DecryptTask : public GMPTask
-  {
-    DecryptTask(ClearKeyDecryptor* aTarget, GMPBuffer* aBuffer,
-                GMPEncryptedBufferMetadata* aMetadata)
-      : mTarget(aTarget), mBuffer(aBuffer), mMetadata(aMetadata) { }
-
-    virtual void Run() MOZ_OVERRIDE
-    {
-      mTarget->Decrypt(mBuffer, mMetadata);
-    }
-
-    virtual void Destroy() MOZ_OVERRIDE {
-      delete this;
-    }
-
-    virtual ~DecryptTask() { }
-
-    ClearKeyDecryptor* mTarget;
-    GMPBuffer* mBuffer;
-    GMPEncryptedBufferMetadata* mMetadata;
-  };
-
-  struct DestroyTask : public GMPTask
-  {
-    explicit DestroyTask(ClearKeyDecryptor* aTarget) : mTarget(aTarget) { }
-
-    virtual void Run() MOZ_OVERRIDE {
-      delete mTarget;
-    }
-
-    virtual void Destroy() MOZ_OVERRIDE {
-      delete this;
-    }
-
-    virtual ~DestroyTask() { }
-
-    ClearKeyDecryptor* mTarget;
-  };
-
-  void Decrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
-
-  uint32_t mRefCnt;
-
-  GMPDecryptorCallback* mCallback;
-  GMPThread* mThread;
+  ~ClearKeyDecryptor();
 
   Key mKey;
 };
 
+
+/* static */ ClearKeyDecryptionManager* ClearKeyDecryptionManager::sInstance = nullptr;
+
+/* static */ ClearKeyDecryptionManager*
+ClearKeyDecryptionManager::Get()
+{
+  if (!sInstance) {
+    sInstance = new ClearKeyDecryptionManager();
+  }
+  return sInstance;
+}
+
 ClearKeyDecryptionManager::ClearKeyDecryptionManager()
 {
-  CK_LOGD("ClearKeyDecryptionManager ctor");
-
-  // The ClearKeyDecryptionManager maintains a self reference which is
-  // removed when the host is finished with the interface and calls
-  // DecryptingComplete(). We make ClearKeyDecryptionManager refcounted so
-  // that the tasks to that we dispatch to call functions on it won't end up
-  // derefing a null reference after DecryptingComplete() is called.
-  AddRef();
+  CK_LOGD("ClearKeyDecryptionManager::ClearKeyDecryptionManager");
 }
 
 ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
 {
-  CK_LOGD("ClearKeyDecryptionManager dtor");
-  MOZ_ASSERT(mRefCount == 1);
+  CK_LOGD("ClearKeyDecryptionManager::~ClearKeyDecryptionManager");
+
+  sInstance = nullptr;
+
+  for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
+    it->second->Release();
+  }
+  mDecryptors.clear();
 }
 
-void
-ClearKeyDecryptionManager::Init(GMPDecryptorCallback* aCallback)
+bool
+ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const
 {
-  CK_LOGD("ClearKeyDecryptionManager::Init");
-  mCallback = aCallback;
-  mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
-                             GMP_EME_CAP_DECRYPT_VIDEO);
-  ClearKeyPersistence::EnsureInitialized();
+  CK_LOGD("ClearKeyDecryptionManager::HasSeenKeyId");
+  return mDecryptors.find(aKeyId) != mDecryptors.end();
 }
 
-void
-ClearKeyDecryptionManager::CreateSession(uint32_t aCreateSessionToken,
-                                         uint32_t aPromiseId,
-                                         const char* aInitDataType,
-                                         uint32_t aInitDataTypeSize,
-                                         const uint8_t* aInitData,
-                                         uint32_t aInitDataSize,
-                                         GMPSessionType aSessionType)
+bool
+ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const
 {
-  CK_LOGD("ClearKeyDecryptionManager::CreateSession type:%s", aInitDataType);
-
-  // initDataType must be "cenc".
-  if (strcmp("cenc", aInitDataType)) {
-    mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
-                             nullptr /* message */, 0 /* messageLen */);
-    return;
-  }
-
-  if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
-                                                        aCreateSessionToken,
-                                                        aPromiseId,
-                                                        aInitData,
-                                                        aInitDataSize,
-                                                        aSessionType)) {
-    return;
-  }
-
-  string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType);
-  MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end());
-
-  ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
-  session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize);
-  mSessions[sessionId] = session;
-
-  const vector<KeyId>& sessionKeys = session->GetKeyIds();
-  vector<KeyId> neededKeys;
-  for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
-    if (!Contains(mDecryptors, *it)) {
-      // Need to request this key ID from the client.
-      neededKeys.push_back(*it);
-    } else {
-      // We already have a key for this key ID. Mark as usable.
-      mCallback->KeyIdUsable(sessionId.c_str(), sessionId.length(),
-                             &(*it)[0], it->size());
-    }
-  }
-
-  if (neededKeys.empty()) {
-    CK_LOGD("No keys needed from client.");
-    return;
-  }
-
-  // Send a request for needed key data.
-  string request;
-  ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
-  mCallback->SessionMessage(&sessionId[0], sessionId.length(),
-                            (uint8_t*)&request[0], request.length(),
-                            "" /* destination url */, 0);
+  CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForKeyId");
+  const auto& decryptor = mDecryptors.find(aKeyId);
+  return decryptor != mDecryptors.end() && !decryptor->second->HasKey();
 }
 
-void
-ClearKeyDecryptionManager::LoadSession(uint32_t aPromiseId,
-                                       const char* aSessionId,
-                                       uint32_t aSessionIdLength)
+bool
+ClearKeyDecryptionManager::HasKeyForKeyId(const KeyId& aKeyId) const
 {
-  CK_LOGD("ClearKeyDecryptionManager::LoadSession");
-
-  if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
-    return;
-  }
+  CK_LOGD("ClearKeyDecryptionManager::HasKeyForKeyId");
+  const auto& decryptor = mDecryptors.find(aKeyId);
+  return decryptor != mDecryptors.end() && decryptor->second->HasKey();
+}
 
-  if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
-                                                      aPromiseId,
-                                                      aSessionId,
-                                                      aSessionIdLength)) {
-    return;
-  }
-
-  string sid(aSessionId, aSessionId + aSessionIdLength);
-  if (!ClearKeyPersistence::IsPersistentSessionId(sid)) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
-    return;
-  }
-
-  // Callsback PersistentSessionDataLoaded with results...
-  ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
+const Key&
+ClearKeyDecryptionManager::GetDecryptionKey(const KeyId& aKeyId)
+{
+  MOZ_ASSERT(HasKeyForKeyId(aKeyId));
+  return mDecryptors[aKeyId]->DecryptionKey();
 }
 
 void
-ClearKeyDecryptionManager::PersistentSessionDataLoaded(GMPErr aStatus,
-                                                       uint32_t aPromiseId,
-                                                       const string& aSessionId,
-                                                       const uint8_t* aKeyData,
-                                                       uint32_t aKeyDataSize)
-{
-  if (GMP_FAILED(aStatus) ||
-      Contains(mSessions, aSessionId) ||
-      (aKeyDataSize % (2 * CLEARKEY_KEY_LEN)) != 0) {
-    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
-    return;
-  }
-
-  ClearKeySession* session = new ClearKeySession(aSessionId,
-                                                 mCallback,
-                                                 kGMPPersistentSession);
-  mSessions[aSessionId] = session;
-
-  uint32_t numKeys = aKeyDataSize / (2 * CLEARKEY_KEY_LEN);
-  for (uint32_t i = 0; i < numKeys; i ++) {
-    const uint8_t* base = aKeyData + 2 * CLEARKEY_KEY_LEN * i;
-
-    KeyId keyId(base, base + CLEARKEY_KEY_LEN);
-    MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
-
-    Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN);
-    MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
-
-    session->AddKeyId(keyId);
-
-    if (!Contains(mDecryptors, keyId)) {
-      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, key);
-    }
-    mDecryptors[keyId]->AddRef();
-    mCallback->KeyIdUsable(aSessionId.c_str(), aSessionId.size(),
-                           &keyId[0], keyId.size());
-  }
-
-  mCallback->ResolveLoadSessionPromise(aPromiseId, true);
-}
-
-void
-ClearKeyDecryptionManager::UpdateSession(uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength,
-                                         const uint8_t* aResponse,
-                                         uint32_t aResponseSize)
+ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey)
 {
-  CK_LOGD("ClearKeyDecryptionManager::UpdateSession");
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
-
-  auto itr = mSessions.find(sessionId);
-  if (itr == mSessions.end() || !(itr->second)) {
-    CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
-    mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
-    return;
-  }
-  ClearKeySession* session = itr->second;
-
-  // Parse the response for any (key ID, key) pairs.
-  vector<KeyIdPair> keyPairs;
-  if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
-    CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
-    mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0);
-    return;
-  }
-
-  for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) {
-    KeyId& keyId = it->mKeyId;
-
-    if (!Contains(mDecryptors, keyId)) {
-      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, it->mKey);
-      mCallback->KeyIdUsable(aSessionId, aSessionIdLength,
-                             &keyId[0], keyId.size());
-    }
-
-    mDecryptors[keyId]->AddRef();
-  }
-
-  if (session->Type() != kGMPPersistentSession) {
-    mCallback->ResolvePromise(aPromiseId);
-    return;
-  }
-
-  // Store the keys on disk. We store a record whose name is the sessionId,
-  // and simply append each keyId followed by its key.
-  vector<uint8_t> keydata;
-  Serialize(session, keydata);
-  GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
-  static const char* message = "Couldn't store cenc key init data";
-  GMPTask* reject = WrapTask(mCallback,
-                             &GMPDecryptorCallback::RejectPromise,
-                             aPromiseId,
-                             kGMPInvalidStateError,
-                             message,
-                             strlen(message));
-  StoreData(sessionId, keydata, resolve, reject);
-}
-
-void
-ClearKeyDecryptionManager::Serialize(const ClearKeySession* aSession,
-                                     std::vector<uint8_t>& aOutKeyData)
-{
-  const std::vector<KeyId>& keyIds = aSession->GetKeyIds();
-  for (size_t i = 0; i < keyIds.size(); i++) {
-    const KeyId& keyId = keyIds[i];
-    if (!Contains(mDecryptors, keyId)) {
-      continue;
-    }
-    MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
-    aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end());
-    const Key& key = mDecryptors[keyId]->DecryptionKey();
-    MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
-    aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end());
+  CK_LOGD("ClearKeyDecryptionManager::InitKey");
+  if (IsExpectingKeyForKeyId(aKeyId)) {
+    mDecryptors[aKeyId]->InitKey(aKey);
   }
 }
 
 void
-ClearKeyDecryptionManager::CloseSession(uint32_t aPromiseId,
-                                        const char* aSessionId,
-                                        uint32_t aSessionIdLength)
+ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId)
 {
-  CK_LOGD("ClearKeyDecryptionManager::CloseSession");
-
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
-  auto itr = mSessions.find(sessionId);
-  if (itr == mSessions.end()) {
-    CK_LOGW("ClearKey CDM couldn't close non-existent session.");
-    mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
-    return;
+  CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId");
+  if (!HasSeenKeyId(aKeyId)) {
+    mDecryptors[aKeyId] = new ClearKeyDecryptor();
   }
-
-  ClearKeySession* session = itr->second;
-  MOZ_ASSERT(session);
-
-  ClearInMemorySessionData(session);
-  mCallback->ResolvePromise(aPromiseId);
-  mCallback->SessionClosed(aSessionId, aSessionIdLength);
-}
-
-void
-ClearKeyDecryptionManager::ClearInMemorySessionData(ClearKeySession* aSession)
-{
-  MOZ_ASSERT(aSession);
-
-  const vector<KeyId>& keyIds = aSession->GetKeyIds();
-  for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
-    MOZ_ASSERT(Contains(mDecryptors, *it));
-    if (!mDecryptors[*it]->Release()) {
-      mDecryptors.erase(*it);
-      mCallback->KeyIdNotUsable(aSession->Id().c_str(), aSession->Id().size(),
-                                &(*it)[0], it->size());
-    }
-  }
-
-  mSessions.erase(aSession->Id());
-  delete aSession;
+  mDecryptors[aKeyId]->AddRef();
 }
 
 void
-ClearKeyDecryptionManager::RemoveSession(uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength)
+ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId)
 {
-  string sessionId(aSessionId, aSessionId + aSessionIdLength);
-  auto itr = mSessions.find(sessionId);
-  if (itr == mSessions.end()) {
-    CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
-    mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
-    return;
+  CK_LOGD("ClearKeyDecryptionManager::ReleaseKeyId");
+  ClearKeyDecryptor* decryptor = mDecryptors[aKeyId];
+  if (!decryptor->Release()) {
+    mDecryptors.erase(aKeyId);
   }
-
-  ClearKeySession* session = itr->second;
-  MOZ_ASSERT(session);
-  string sid = session->Id();
-  bool isPersistent = session->Type() == kGMPPersistentSession;
-  ClearInMemorySessionData(session);
-
-  if (!isPersistent) {
-    mCallback->ResolvePromise(aPromiseId);
-    return;
-  }
-
-  ClearKeyPersistence::PersistentSessionRemoved(sid);
-
-  // Overwrite the record storing the sessionId's key data with a zero
-  // length record to delete it.
-  vector<uint8_t> emptyKeydata;
-  GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
-  static const char* message = "Could not remove session";
-  GMPTask* reject = WrapTask(mCallback,
-                             &GMPDecryptorCallback::RejectPromise,
-                             aPromiseId,
-                             kGMPInvalidAccessError,
-                             message,
-                             strlen(message));
-  StoreData(sessionId, emptyKeydata, resolve, reject);
 }
 
-void
-ClearKeyDecryptionManager::SetServerCertificate(uint32_t aPromiseId,
-                                                const uint8_t* aServerCert,
-                                                uint32_t aServerCertSize)
-{
-  // ClearKey CDM doesn't support this method by spec.
-  CK_LOGD("ClearKeyDecryptionManager::SetServerCertificate");
-  mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
-                           nullptr /* message */, 0 /* messageLen */);
-}
-
-void
-ClearKeyDecryptionManager::Decrypt(GMPBuffer* aBuffer,
+GMPErr
+ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
                                    GMPEncryptedBufferMetadata* aMetadata)
 {
   CK_LOGD("ClearKeyDecryptionManager::Decrypt");
   KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize());
 
-  if (!Contains(mDecryptors, keyId)) {
-    CK_LOGD("ClearKeyDecryptionManager::Decrypt GMPNoKeyErr");
-    mCallback->Decrypted(aBuffer, GMPNoKeyErr);
-    return;
+  if (!HasKeyForKeyId(keyId)) {
+    return GMPNoKeyErr;
   }
 
-  mDecryptors[keyId]->QueueDecrypt(aBuffer, aMetadata);
+  return mDecryptors[keyId]->Decrypt(aBuffer, aBufferSize, aMetadata);
+}
+
+ClearKeyDecryptor::ClearKeyDecryptor()
+{
+  CK_LOGD("ClearKeyDecryptor ctor");
+}
+
+ClearKeyDecryptor::~ClearKeyDecryptor()
+{
+  CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]);
 }
 
 void
-ClearKeyDecryptionManager::DecryptingComplete()
+ClearKeyDecryptor::InitKey(const Key& aKey)
 {
-  CK_LOGD("ClearKeyDecryptionManager::DecryptingComplete");
-
-  for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
-    delete it->second;
-  }
-  mSessions.clear();
-
-  for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
-    delete it->second;
-  }
-  mDecryptors.clear();
-
-  Release();
+  mKey = aKey;
 }
 
-void
-ClearKeyDecryptor::QueueDecrypt(GMPBuffer* aBuffer,
-                                GMPEncryptedBufferMetadata* aMetadata)
-{
-  CK_LOGD("ClearKeyDecryptor::QueueDecrypt");
-  mThread->Post(new DecryptTask(this, aBuffer, aMetadata));
-}
-
-void
-ClearKeyDecryptor::Decrypt(GMPBuffer* aBuffer,
+GMPErr
+ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
                            GMPEncryptedBufferMetadata* aMetadata)
 {
-  if (!mThread) {
-    mCallback->Decrypted(aBuffer, GMPGenericErr);
-  }
-
+  CK_LOGD("ClearKeyDecryptor::Decrypt");
   // If the sample is split up into multiple encrypted subsamples, we need to
   // stitch them into one continuous buffer for decryption.
-  vector<uint8_t> tmp(aBuffer->Size());
+  std::vector<uint8_t> tmp(aBufferSize);
 
   if (aMetadata->NumSubsamples()) {
     // Take all encrypted parts of subsamples and stitch them into one
     // continuous encrypted buffer.
-    unsigned char* data = aBuffer->Data();
+    unsigned char* data = aBuffer;
     unsigned char* iter = &tmp[0];
     for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) {
       data += aMetadata->ClearBytes()[i];
       uint32_t cipherBytes = aMetadata->CipherBytes()[i];
 
       memcpy(iter, data, cipherBytes);
 
       data += cipherBytes;
       iter += cipherBytes;
     }
 
     tmp.resize((size_t)(iter - &tmp[0]));
   } else {
-    memcpy(&tmp[0], aBuffer->Data(), aBuffer->Size());
+    memcpy(&tmp[0], aBuffer, aBufferSize);
   }
 
   MOZ_ASSERT(aMetadata->IVSize() == 8 || aMetadata->IVSize() == 16);
-  vector<uint8_t> iv(aMetadata->IV(), aMetadata->IV() + aMetadata->IVSize());
+  std::vector<uint8_t> iv(aMetadata->IV(), aMetadata->IV() + aMetadata->IVSize());
   iv.insert(iv.end(), CLEARKEY_KEY_LEN - aMetadata->IVSize(), 0);
 
   ClearKeyUtils::DecryptAES(mKey, tmp, iv);
 
   if (aMetadata->NumSubsamples()) {
     // Take the decrypted buffer, split up into subsamples, and insert those
     // subsamples back into their original position in the original buffer.
-    unsigned char* data = aBuffer->Data();
+    unsigned char* data = aBuffer;
     unsigned char* iter = &tmp[0];
     for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) {
       data += aMetadata->ClearBytes()[i];
       uint32_t cipherBytes = aMetadata->CipherBytes()[i];
 
       memcpy(data, iter, cipherBytes);
 
       data += cipherBytes;
       iter += cipherBytes;
     }
   } else {
-    memcpy(aBuffer->Data(), &tmp[0], aBuffer->Size());
+    memcpy(aBuffer, &tmp[0], aBufferSize);
   }
 
-  mCallback->Decrypted(aBuffer, GMPNoErr);
-}
-
-ClearKeyDecryptor::ClearKeyDecryptor(GMPDecryptorCallback* aCallback,
-                                     const Key& aKey)
-  : mRefCnt(0)
-  , mCallback(aCallback)
-  , mThread(nullptr)
-  , mKey(aKey)
-{
-  if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
-    CK_LOGD("failed to create thread in clearkey cdm");
-    mThread = nullptr;
-    return;
-  }
-}
-
-ClearKeyDecryptor::~ClearKeyDecryptor()
-{
-  CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]);
+  return GMPNoErr;
 }
-
-uint32_t
-ClearKeyDecryptor::AddRef()
-{
-  return ++mRefCnt;
-}
-
-uint32_t
-ClearKeyDecryptor::Release()
-{
-  uint32_t newCount = --mRefCnt;
-  if (!newCount) {
-    if (mThread) {
-      // Shutdown mThread. We cache a pointer to mThread, as the DestroyTask
-      // may run and delete |this| before Post() returns.
-      GMPThread* thread = mThread;
-      thread->Post(new DestroyTask(this));
-      thread->Join();
-    } else {
-      delete this;
-    }
-  }
-
-  return newCount;
-}
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
@@ -1,80 +1,47 @@
 /* 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/. */
 
-#ifndef __ClearKeyDecryptor_h__
-#define __ClearKeyDecryptor_h__
+#ifndef __ClearKeyDecryptionManager_h__
+#define __ClearKeyDecryptionManager_h__
 
 #include <map>
-#include <string>
-#include <vector>
 
-#include "ClearKeySession.h"
 #include "ClearKeyUtils.h"
-#include "gmp-api/gmp-decryption.h"
-#include "ScopedNSSTypes.h"
 #include "RefCounted.h"
 
 class ClearKeyDecryptor;
-class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
-                                          , public RefCounted
-{
-public:
-  ClearKeyDecryptionManager();
-
-  virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
 
-  virtual void CreateSession(uint32_t aCreateSessionToken,
-                             uint32_t aPromiseId,
-                             const char* aInitDataType,
-                             uint32_t aInitDataTypeSize,
-                             const uint8_t* aInitData,
-                             uint32_t aInitDataSize,
-                             GMPSessionType aSessionType) MOZ_OVERRIDE;
+class ClearKeyDecryptionManager : public RefCounted
+{
+private:
+  ClearKeyDecryptionManager();
+  ~ClearKeyDecryptionManager();
 
-  virtual void LoadSession(uint32_t aPromiseId,
-                           const char* aSessionId,
-                           uint32_t aSessionIdLength) MOZ_OVERRIDE;
+  static ClearKeyDecryptionManager* sInstance;
+
+public:
+  static ClearKeyDecryptionManager* Get();
 
-  virtual void UpdateSession(uint32_t aPromiseId,
-                             const char* aSessionId,
-                             uint32_t aSessionIdLength,
-                             const uint8_t* aResponse,
-                             uint32_t aResponseSize) MOZ_OVERRIDE;
+  bool HasSeenKeyId(const KeyId& aKeyId) const;
+  bool HasKeyForKeyId(const KeyId& aKeyId) const;
 
-  virtual void CloseSession(uint32_t aPromiseId,
-                            const char* aSessionId,
-                            uint32_t aSessionIdLength) MOZ_OVERRIDE;
-
-  virtual void RemoveSession(uint32_t aPromiseId,
-                             const char* aSessionId,
-                             uint32_t aSessionIdLength) MOZ_OVERRIDE;
+  const Key& GetDecryptionKey(const KeyId& aKeyId);
 
-  virtual void SetServerCertificate(uint32_t aPromiseId,
-                                    const uint8_t* aServerCert,
-                                    uint32_t aServerCertSize) MOZ_OVERRIDE;
-
-  virtual void Decrypt(GMPBuffer* aBuffer,
-                       GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE;
+  // Create a decryptor for the given KeyId if one does not already exist.
+  void InitKey(KeyId aKeyId, Key aKey);
+  void ExpectKeyId(KeyId aKeyId);
+  void ReleaseKeyId(KeyId aKeyId);
 
-  virtual void DecryptingComplete() MOZ_OVERRIDE;
+  GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
+                 GMPEncryptedBufferMetadata* aMetadata);
 
-  void PersistentSessionDataLoaded(GMPErr aStatus,
-                                   uint32_t aPromiseId,
-                                   const std::string& aSessionId,
-                                   const uint8_t* aKeyData,
-                                   uint32_t aKeyDataSize);
+  void Shutdown();
 
 private:
-  ~ClearKeyDecryptionManager();
-
-  void ClearInMemorySessionData(ClearKeySession* aSession);
-  void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
-
-  GMPDecryptorCallback* mCallback;
+  bool IsExpectingKeyForKeyId(const KeyId& aKeyId) const;
 
   std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
-  std::map<std::string, ClearKeySession*> mSessions;
 };
 
-#endif // __ClearKeyDecryptor_h__
+#endif // __ClearKeyDecryptionManager_h__
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -1,16 +1,16 @@
 /* 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 "ClearKeyPersistence.h"
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
-#include "ClearKeyDecryptionManager.h"
+#include "ClearKeySessionManager.h"
 #include "mozilla/RefPtr.h"
 
 #include <stdint.h>
 #include <set>
 #include <vector>
 #include <sstream>
 
 using namespace mozilla;
@@ -92,17 +92,17 @@ ClearKeyPersistence::GetNewSessionId(GMP
   sNextSessionId++;
 
   return sessionId;
 }
 
 
 class CreateSessionTask : public GMPTask {
 public:
-  CreateSessionTask(ClearKeyDecryptionManager* aTarget,
+  CreateSessionTask(ClearKeySessionManager* aTarget,
                     uint32_t aCreateSessionToken,
                     uint32_t aPromiseId,
                     const uint8_t* aInitData,
                     uint32_t aInitDataSize,
                     GMPSessionType aSessionType)
     : mTarget(aTarget)
     , mCreateSessionToken(aCreateSessionToken)
     , mPromiseId(aPromiseId)
@@ -120,26 +120,26 @@ public:
                            &mInitData.front(),
                            mInitData.size(),
                            mSessionType);
   }
   virtual void Destroy() MOZ_OVERRIDE {
     delete this;
   }
 private:
-  RefPtr<ClearKeyDecryptionManager> mTarget;
+  RefPtr<ClearKeySessionManager> mTarget;
   uint32_t mCreateSessionToken;
   uint32_t mPromiseId;
   vector<uint8_t> mInitData;
   GMPSessionType mSessionType;
 };
 
 
 /* static */ bool
-ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
                                                   uint32_t aCreateSessionToken,
                                                   uint32_t aPromiseId,
                                                   const uint8_t* aInitData,
                                                   uint32_t aInitDataSize,
                                                   GMPSessionType aSessionType)
 {
   if (sPersistentKeyState >= LOADED)  {
     return false;
@@ -151,17 +151,17 @@ ClearKeyPersistence::DeferCreateSessionI
                                      aInitDataSize,
                                      aSessionType);
   sTasksBlockedOnSessionIdLoad.push_back(t);
   return true;
 }
 
 class LoadSessionTask : public GMPTask {
 public:
-  LoadSessionTask(ClearKeyDecryptionManager* aTarget,
+  LoadSessionTask(ClearKeySessionManager* aTarget,
                   uint32_t aPromiseId,
                   const char* aSessionId,
                   uint32_t aSessionIdLength)
     : mTarget(aTarget)
     , mPromiseId(aPromiseId)
     , mSessionId(aSessionId, aSessionId + aSessionIdLength)
   {
   }
@@ -169,23 +169,23 @@ public:
     mTarget->LoadSession(mPromiseId,
                          mSessionId.c_str(),
                          mSessionId.size());
   }
   virtual void Destroy() MOZ_OVERRIDE {
     delete this;
   }
 private:
-  RefPtr<ClearKeyDecryptionManager> mTarget;
+  RefPtr<ClearKeySessionManager> mTarget;
   uint32_t mPromiseId;
   string mSessionId;
 };
 
 /* static */ bool
-ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
                                                 uint32_t aPromiseId,
                                                 const char* aSessionId,
                                                 uint32_t aSessionIdLength)
 {
   if (sPersistentKeyState >= LOADED)  {
     return false;
   }
   GMPTask* t = new LoadSessionTask(aInstance,
@@ -199,39 +199,39 @@ ClearKeyPersistence::DeferLoadSessionIfN
 /* static */ bool
 ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId)
 {
   return Contains(sPersistentSessionIds, atoi(aSessionId.c_str()));
 }
 
 class LoadSessionFromKeysTask : public ReadContinuation {
 public:
-  LoadSessionFromKeysTask(ClearKeyDecryptionManager* aTarget,
+  LoadSessionFromKeysTask(ClearKeySessionManager* aTarget,
                           const string& aSessionId,
                           uint32_t aPromiseId)
     : mTarget(aTarget)
     , mSessionId(aSessionId)
     , mPromiseId(aPromiseId)
   {
   }
 
   virtual void ReadComplete(GMPErr aStatus,
                             const uint8_t* aData,
                             uint32_t aLength) MOZ_OVERRIDE
   {
     mTarget->PersistentSessionDataLoaded(aStatus, mPromiseId, mSessionId, aData, aLength);
   }
 private:
-  RefPtr<ClearKeyDecryptionManager> mTarget;
+  RefPtr<ClearKeySessionManager> mTarget;
   string mSessionId;
   uint32_t mPromiseId;
 };
 
 /* static */ void
-ClearKeyPersistence::LoadSessionData(ClearKeyDecryptionManager* aInstance,
+ClearKeyPersistence::LoadSessionData(ClearKeySessionManager* aInstance,
                                      const string& aSid,
                                      uint32_t aPromiseId)
 {
   LoadSessionFromKeysTask* loadTask =
     new LoadSessionFromKeysTask(aInstance, aSid, aPromiseId);
   ReadData(aSid, loadTask);
 }
 
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -3,38 +3,38 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __ClearKeyPersistence_h__
 #define __ClearKeyPersistence_h__
 
 #include <string>
 #include "gmp-decryption.h"
 
-class ClearKeyDecryptionManager;
+class ClearKeySessionManager;
 
 class ClearKeyPersistence {
 public:
   static void EnsureInitialized();
 
   static std::string GetNewSessionId(GMPSessionType aSessionType);
 
-  static bool DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+  static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance,
                                            uint32_t aCreateSessionToken,
                                            uint32_t aPromiseId,
                                            const uint8_t* aInitData,
                                            uint32_t aInitDataSize,
                                            GMPSessionType aSessionType);
 
-  static bool DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+  static bool DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance,
                                          uint32_t aPromiseId,
                                          const char* aSessionId,
                                          uint32_t aSessionIdLength);
 
   static bool IsPersistentSessionId(const std::string& aSid);
 
-  static void LoadSessionData(ClearKeyDecryptionManager* aInstance,
+  static void LoadSessionData(ClearKeySessionManager* aInstance,
                               const std::string& aSid,
                               uint32_t aPromiseId);
 
   static void PersistentSessionRemoved(const std::string& aSid);
 };
 
 #endif // __ClearKeyPersistence_h__
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -27,17 +27,17 @@ public:
   void Init(uint32_t aCreateSessionToken,
             uint32_t aPromiseId,
             const uint8_t* aInitData, uint32_t aInitDataSize);
 
   GMPSessionType Type() const;
 
   void AddKeyId(const KeyId& aKeyId);
 
-  const std::string Id() const { return mSessionId; }
+  const std::string& Id() const { return mSessionId; }
 
 private:
   const std::string mSessionId;
   std::vector<KeyId> mKeyIds;
 
   GMPDecryptorCallback* mCallback;
   const GMPSessionType mSessionType;
 };
copy from media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
copy to media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -1,129 +1,65 @@
 /* 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 <stdint.h>
 #include <stdio.h>
 
 #include "ClearKeyDecryptionManager.h"
+#include "ClearKeySessionManager.h"
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
 #include "ClearKeyPersistence.h"
 #include "gmp-task-utils.h"
 
 #include "mozilla/Assertions.h"
 
 using namespace mozilla;
 using namespace std;
 
-class ClearKeyDecryptor
+ClearKeySessionManager::ClearKeySessionManager()
+  : mDecryptionManager(ClearKeyDecryptionManager::Get())
 {
-public:
-  ClearKeyDecryptor(GMPDecryptorCallback* aCallback, const Key& aKey);
-  ~ClearKeyDecryptor();
-
-  void InitKey();
-
-  void QueueDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
-
-  uint32_t AddRef();
-  uint32_t Release();
-
-  const Key& DecryptionKey() const { return mKey; }
-
-private:
-  struct DecryptTask : public GMPTask
-  {
-    DecryptTask(ClearKeyDecryptor* aTarget, GMPBuffer* aBuffer,
-                GMPEncryptedBufferMetadata* aMetadata)
-      : mTarget(aTarget), mBuffer(aBuffer), mMetadata(aMetadata) { }
-
-    virtual void Run() MOZ_OVERRIDE
-    {
-      mTarget->Decrypt(mBuffer, mMetadata);
-    }
-
-    virtual void Destroy() MOZ_OVERRIDE {
-      delete this;
-    }
-
-    virtual ~DecryptTask() { }
-
-    ClearKeyDecryptor* mTarget;
-    GMPBuffer* mBuffer;
-    GMPEncryptedBufferMetadata* mMetadata;
-  };
+  CK_LOGD("ClearKeySessionManager ctor");
+  AddRef();
 
-  struct DestroyTask : public GMPTask
-  {
-    explicit DestroyTask(ClearKeyDecryptor* aTarget) : mTarget(aTarget) { }
-
-    virtual void Run() MOZ_OVERRIDE {
-      delete mTarget;
-    }
-
-    virtual void Destroy() MOZ_OVERRIDE {
-      delete this;
-    }
-
-    virtual ~DestroyTask() { }
-
-    ClearKeyDecryptor* mTarget;
-  };
-
-  void Decrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
-
-  uint32_t mRefCnt;
-
-  GMPDecryptorCallback* mCallback;
-  GMPThread* mThread;
-
-  Key mKey;
-};
-
-ClearKeyDecryptionManager::ClearKeyDecryptionManager()
-{
-  CK_LOGD("ClearKeyDecryptionManager ctor");
-
-  // The ClearKeyDecryptionManager maintains a self reference which is
-  // removed when the host is finished with the interface and calls
-  // DecryptingComplete(). We make ClearKeyDecryptionManager refcounted so
-  // that the tasks to that we dispatch to call functions on it won't end up
-  // derefing a null reference after DecryptingComplete() is called.
-  AddRef();
+  if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
+    CK_LOGD("failed to create thread in clearkey cdm");
+    mThread = nullptr;
+  }
 }
 
-ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
+ClearKeySessionManager::~ClearKeySessionManager()
 {
-  CK_LOGD("ClearKeyDecryptionManager dtor");
-  MOZ_ASSERT(mRefCount == 1);
+  CK_LOGD("ClearKeySessionManager dtor");
+   MOZ_ASSERT(!mRefCount);
 }
 
 void
-ClearKeyDecryptionManager::Init(GMPDecryptorCallback* aCallback)
+ClearKeySessionManager::Init(GMPDecryptorCallback* aCallback)
 {
-  CK_LOGD("ClearKeyDecryptionManager::Init");
+  CK_LOGD("ClearKeySessionManager::Init");
   mCallback = aCallback;
   mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
                              GMP_EME_CAP_DECRYPT_VIDEO);
   ClearKeyPersistence::EnsureInitialized();
 }
 
 void
-ClearKeyDecryptionManager::CreateSession(uint32_t aCreateSessionToken,
-                                         uint32_t aPromiseId,
-                                         const char* aInitDataType,
-                                         uint32_t aInitDataTypeSize,
-                                         const uint8_t* aInitData,
-                                         uint32_t aInitDataSize,
-                                         GMPSessionType aSessionType)
+ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken,
+                                      uint32_t aPromiseId,
+                                      const char* aInitDataType,
+                                      uint32_t aInitDataTypeSize,
+                                      const uint8_t* aInitData,
+                                      uint32_t aInitDataSize,
+                                      GMPSessionType aSessionType)
 {
-  CK_LOGD("ClearKeyDecryptionManager::CreateSession type:%s", aInitDataType);
+  CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType);
 
   // initDataType must be "cenc".
   if (strcmp("cenc", aInitDataType)) {
     mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
                              nullptr /* message */, 0 /* messageLen */);
     return;
   }
 
@@ -141,45 +77,43 @@ ClearKeyDecryptionManager::CreateSession
 
   ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
   session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize);
   mSessions[sessionId] = session;
 
   const vector<KeyId>& sessionKeys = session->GetKeyIds();
   vector<KeyId> neededKeys;
   for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
-    if (!Contains(mDecryptors, *it)) {
-      // Need to request this key ID from the client.
-      neededKeys.push_back(*it);
-    } else {
-      // We already have a key for this key ID. Mark as usable.
-      mCallback->KeyIdUsable(sessionId.c_str(), sessionId.length(),
-                             &(*it)[0], it->size());
-    }
+    // Need to request this key ID from the client. We always send a key
+    // request, whether or not another session has sent a request with the same
+    // key ID. Otherwise a script can end up waiting for another script to
+    // respond to the request (which may not necessarily happen).
+    neededKeys.push_back(*it);
+    mDecryptionManager->ExpectKeyId(*it);
   }
 
   if (neededKeys.empty()) {
     CK_LOGD("No keys needed from client.");
     return;
   }
 
   // Send a request for needed key data.
   string request;
   ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
   mCallback->SessionMessage(&sessionId[0], sessionId.length(),
                             (uint8_t*)&request[0], request.length(),
                             "" /* destination url */, 0);
 }
 
 void
-ClearKeyDecryptionManager::LoadSession(uint32_t aPromiseId,
-                                       const char* aSessionId,
-                                       uint32_t aSessionIdLength)
+ClearKeySessionManager::LoadSession(uint32_t aPromiseId,
+                                    const char* aSessionId,
+                                    uint32_t aSessionIdLength)
 {
-  CK_LOGD("ClearKeyDecryptionManager::LoadSession");
+  CK_LOGD("ClearKeySessionManager::LoadSession");
 
   if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
     mCallback->ResolveLoadSessionPromise(aPromiseId, false);
     return;
   }
 
   if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
                                                       aPromiseId,
@@ -194,22 +128,23 @@ ClearKeyDecryptionManager::LoadSession(u
     return;
   }
 
   // Callsback PersistentSessionDataLoaded with results...
   ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
 }
 
 void
-ClearKeyDecryptionManager::PersistentSessionDataLoaded(GMPErr aStatus,
-                                                       uint32_t aPromiseId,
-                                                       const string& aSessionId,
-                                                       const uint8_t* aKeyData,
-                                                       uint32_t aKeyDataSize)
+ClearKeySessionManager::PersistentSessionDataLoaded(GMPErr aStatus,
+                                                    uint32_t aPromiseId,
+                                                    const string& aSessionId,
+                                                    const uint8_t* aKeyData,
+                                                    uint32_t aKeyDataSize)
 {
+  CK_LOGD("ClearKeySessionManager::PersistentSessionDataLoaded");
   if (GMP_FAILED(aStatus) ||
       Contains(mSessions, aSessionId) ||
       (aKeyDataSize % (2 * CLEARKEY_KEY_LEN)) != 0) {
     mCallback->ResolveLoadSessionPromise(aPromiseId, false);
     return;
   }
 
   ClearKeySession* session = new ClearKeySession(aSessionId,
@@ -224,35 +159,34 @@ ClearKeyDecryptionManager::PersistentSes
     KeyId keyId(base, base + CLEARKEY_KEY_LEN);
     MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
 
     Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN);
     MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
 
     session->AddKeyId(keyId);
 
-    if (!Contains(mDecryptors, keyId)) {
-      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, key);
-    }
-    mDecryptors[keyId]->AddRef();
-    mCallback->KeyIdUsable(aSessionId.c_str(), aSessionId.size(),
+    mDecryptionManager->ExpectKeyId(keyId);
+    mDecryptionManager->InitKey(keyId, key);
+    mKeyIds.insert(key);
+    mCallback->KeyIdUsable(&aSessionId[0], aSessionId.size(),
                            &keyId[0], keyId.size());
   }
 
   mCallback->ResolveLoadSessionPromise(aPromiseId, true);
 }
 
 void
-ClearKeyDecryptionManager::UpdateSession(uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength,
-                                         const uint8_t* aResponse,
-                                         uint32_t aResponseSize)
+ClearKeySessionManager::UpdateSession(uint32_t aPromiseId,
+                                      const char* aSessionId,
+                                      uint32_t aSessionIdLength,
+                                      const uint8_t* aResponse,
+                                      uint32_t aResponseSize)
 {
-  CK_LOGD("ClearKeyDecryptionManager::UpdateSession");
+  CK_LOGD("ClearKeySessionManager::UpdateSession");
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
 
   auto itr = mSessions.find(sessionId);
   if (itr == mSessions.end() || !(itr->second)) {
     CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
     mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
     return;
   }
@@ -262,25 +196,20 @@ ClearKeyDecryptionManager::UpdateSession
   vector<KeyIdPair> keyPairs;
   if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
     CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
     mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0);
     return;
   }
 
   for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) {
-    KeyId& keyId = it->mKeyId;
-
-    if (!Contains(mDecryptors, keyId)) {
-      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, it->mKey);
-      mCallback->KeyIdUsable(aSessionId, aSessionIdLength,
-                             &keyId[0], keyId.size());
-    }
-
-    mDecryptors[keyId]->AddRef();
+    mDecryptionManager->InitKey(it->mKeyId, it->mKey);
+    mKeyIds.insert(it->mKeyId);
+    mCallback->KeyIdUsable(aSessionId, aSessionIdLength,
+                           &it->mKeyId[0], it->mKeyId.size());
   }
 
   if (session->Type() != kGMPPersistentSession) {
     mCallback->ResolvePromise(aPromiseId);
     return;
   }
 
   // Store the keys on disk. We store a record whose name is the sessionId,
@@ -294,39 +223,39 @@ ClearKeyDecryptionManager::UpdateSession
                              aPromiseId,
                              kGMPInvalidStateError,
                              message,
                              strlen(message));
   StoreData(sessionId, keydata, resolve, reject);
 }
 
 void
-ClearKeyDecryptionManager::Serialize(const ClearKeySession* aSession,
-                                     std::vector<uint8_t>& aOutKeyData)
+ClearKeySessionManager::Serialize(const ClearKeySession* aSession,
+                                  std::vector<uint8_t>& aOutKeyData)
 {
   const std::vector<KeyId>& keyIds = aSession->GetKeyIds();
   for (size_t i = 0; i < keyIds.size(); i++) {
     const KeyId& keyId = keyIds[i];
-    if (!Contains(mDecryptors, keyId)) {
+    if (!mDecryptionManager->HasKeyForKeyId(keyId)) {
       continue;
     }
     MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
     aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end());
-    const Key& key = mDecryptors[keyId]->DecryptionKey();
+    const Key& key = mDecryptionManager->GetDecryptionKey(keyId);
     MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
     aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end());
   }
 }
 
 void
-ClearKeyDecryptionManager::CloseSession(uint32_t aPromiseId,
-                                        const char* aSessionId,
-                                        uint32_t aSessionIdLength)
+ClearKeySessionManager::CloseSession(uint32_t aPromiseId,
+                                     const char* aSessionId,
+                                     uint32_t aSessionIdLength)
 {
-  CK_LOGD("ClearKeyDecryptionManager::CloseSession");
+  CK_LOGD("ClearKeySessionManager::CloseSession");
 
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
   auto itr = mSessions.find(sessionId);
   if (itr == mSessions.end()) {
     CK_LOGW("ClearKey CDM couldn't close non-existent session.");
     mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
     return;
   }
@@ -335,39 +264,40 @@ ClearKeyDecryptionManager::CloseSession(
   MOZ_ASSERT(session);
 
   ClearInMemorySessionData(session);
   mCallback->ResolvePromise(aPromiseId);
   mCallback->SessionClosed(aSessionId, aSessionIdLength);
 }
 
 void
-ClearKeyDecryptionManager::ClearInMemorySessionData(ClearKeySession* aSession)
+ClearKeySessionManager::ClearInMemorySessionData(ClearKeySession* aSession)
 {
   MOZ_ASSERT(aSession);
 
   const vector<KeyId>& keyIds = aSession->GetKeyIds();
   for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
-    MOZ_ASSERT(Contains(mDecryptors, *it));
-    if (!mDecryptors[*it]->Release()) {
-      mDecryptors.erase(*it);
-      mCallback->KeyIdNotUsable(aSession->Id().c_str(), aSession->Id().size(),
-                                &(*it)[0], it->size());
-    }
+    MOZ_ASSERT(mDecryptionManager->HasKeyForKeyId(*it));
+    mDecryptionManager->ReleaseKeyId(*it);
+
+    const string& sessionId = aSession->Id();
+    mCallback->KeyIdNotUsable(&sessionId[0], sessionId.size(),
+                              &(*it)[0], it->size());
   }
 
   mSessions.erase(aSession->Id());
   delete aSession;
 }
 
 void
-ClearKeyDecryptionManager::RemoveSession(uint32_t aPromiseId,
-                                         const char* aSessionId,
-                                         uint32_t aSessionIdLength)
+ClearKeySessionManager::RemoveSession(uint32_t aPromiseId,
+                                      const char* aSessionId,
+                                      uint32_t aSessionIdLength)
 {
+  CK_LOGD("ClearKeySessionManager::RemoveSession");
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
   auto itr = mSessions.find(sessionId);
   if (itr == mSessions.end()) {
     CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
     mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
     return;
   }
 
@@ -394,162 +324,70 @@ ClearKeyDecryptionManager::RemoveSession
                              aPromiseId,
                              kGMPInvalidAccessError,
                              message,
                              strlen(message));
   StoreData(sessionId, emptyKeydata, resolve, reject);
 }
 
 void
-ClearKeyDecryptionManager::SetServerCertificate(uint32_t aPromiseId,
-                                                const uint8_t* aServerCert,
-                                                uint32_t aServerCertSize)
+ClearKeySessionManager::SetServerCertificate(uint32_t aPromiseId,
+                                             const uint8_t* aServerCert,
+                                             uint32_t aServerCertSize)
 {
   // ClearKey CDM doesn't support this method by spec.
-  CK_LOGD("ClearKeyDecryptionManager::SetServerCertificate");
+  CK_LOGD("ClearKeySessionManager::SetServerCertificate");
   mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
                            nullptr /* message */, 0 /* messageLen */);
 }
 
 void
-ClearKeyDecryptionManager::Decrypt(GMPBuffer* aBuffer,
-                                   GMPEncryptedBufferMetadata* aMetadata)
+ClearKeySessionManager::Decrypt(GMPBuffer* aBuffer,
+                                GMPEncryptedBufferMetadata* aMetadata)
 {
-  CK_LOGD("ClearKeyDecryptionManager::Decrypt");
-  KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize());
+  CK_LOGD("ClearKeySessionManager::Decrypt");
 
-  if (!Contains(mDecryptors, keyId)) {
-    CK_LOGD("ClearKeyDecryptionManager::Decrypt GMPNoKeyErr");
-    mCallback->Decrypted(aBuffer, GMPNoKeyErr);
+  if (!mThread) {
+    CK_LOGW("No decrypt thread");
+    mCallback->Decrypted(aBuffer, GMPGenericErr);
     return;
   }
 
-  mDecryptors[keyId]->QueueDecrypt(aBuffer, aMetadata);
+  mThread->Post(WrapTask(this,
+                         &ClearKeySessionManager::DoDecrypt,
+                         aBuffer, aMetadata));
 }
 
 void
-ClearKeyDecryptionManager::DecryptingComplete()
+ClearKeySessionManager::DoDecrypt(GMPBuffer* aBuffer,
+                                  GMPEncryptedBufferMetadata* aMetadata)
 {
-  CK_LOGD("ClearKeyDecryptionManager::DecryptingComplete");
+  CK_LOGD("ClearKeySessionManager::DoDecrypt");
+
+  GMPErr rv = mDecryptionManager->Decrypt(aBuffer->Data(), aBuffer->Size(),
+                                              aMetadata);
+  CK_LOGD("DeDecrypt finished with code %x\n", rv);
+  mCallback->Decrypted(aBuffer, rv);
+}
+
+void
+ClearKeySessionManager::Shutdown()
+{
+  CK_LOGD("ClearKeySessionManager::Shutdown");
 
   for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
     delete it->second;
   }
   mSessions.clear();
-
-  for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
-    delete it->second;
-  }
-  mDecryptors.clear();
-
-  Release();
-}
-
-void
-ClearKeyDecryptor::QueueDecrypt(GMPBuffer* aBuffer,
-                                GMPEncryptedBufferMetadata* aMetadata)
-{
-  CK_LOGD("ClearKeyDecryptor::QueueDecrypt");
-  mThread->Post(new DecryptTask(this, aBuffer, aMetadata));
 }
 
 void
-ClearKeyDecryptor::Decrypt(GMPBuffer* aBuffer,
-                           GMPEncryptedBufferMetadata* aMetadata)
+ClearKeySessionManager::DecryptingComplete()
 {
-  if (!mThread) {
-    mCallback->Decrypted(aBuffer, GMPGenericErr);
-  }
-
-  // If the sample is split up into multiple encrypted subsamples, we need to
-  // stitch them into one continuous buffer for decryption.
-  vector<uint8_t> tmp(aBuffer->Size());
-
-  if (aMetadata->NumSubsamples()) {
-    // Take all encrypted parts of subsamples and stitch them into one
-    // continuous encrypted buffer.
-    unsigned char* data = aBuffer->Data();
-    unsigned char* iter = &tmp[0];
-    for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) {
-      data += aMetadata->ClearBytes()[i];
-      uint32_t cipherBytes = aMetadata->CipherBytes()[i];
-
-      memcpy(iter, data, cipherBytes);
-
-      data += cipherBytes;
-      iter += cipherBytes;
-    }
-
-    tmp.resize((size_t)(iter - &tmp[0]));
-  } else {
-    memcpy(&tmp[0], aBuffer->Data(), aBuffer->Size());
-  }
-
-  MOZ_ASSERT(aMetadata->IVSize() == 8 || aMetadata->IVSize() == 16);
-  vector<uint8_t> iv(aMetadata->IV(), aMetadata->IV() + aMetadata->IVSize());
-  iv.insert(iv.end(), CLEARKEY_KEY_LEN - aMetadata->IVSize(), 0);
-
-  ClearKeyUtils::DecryptAES(mKey, tmp, iv);
-
-  if (aMetadata->NumSubsamples()) {
-    // Take the decrypted buffer, split up into subsamples, and insert those
-    // subsamples back into their original position in the original buffer.
-    unsigned char* data = aBuffer->Data();
-    unsigned char* iter = &tmp[0];
-    for (size_t i = 0; i < aMetadata->NumSubsamples(); i++) {
-      data += aMetadata->ClearBytes()[i];
-      uint32_t cipherBytes = aMetadata->CipherBytes()[i];
-
-      memcpy(data, iter, cipherBytes);
+  CK_LOGD("ClearKeySessionManager::DecryptingComplete");
 
-      data += cipherBytes;
-      iter += cipherBytes;
-    }
-  } else {
-    memcpy(aBuffer->Data(), &tmp[0], aBuffer->Size());
-  }
-
-  mCallback->Decrypted(aBuffer, GMPNoErr);
-}
-
-ClearKeyDecryptor::ClearKeyDecryptor(GMPDecryptorCallback* aCallback,
-                                     const Key& aKey)
-  : mRefCnt(0)
-  , mCallback(aCallback)
-  , mThread(nullptr)
-  , mKey(aKey)
-{
-  if (GetPlatform()->createthread(&mThread) != GMPNoErr) {
-    CK_LOGD("failed to create thread in clearkey cdm");
-    mThread = nullptr;
-    return;
-  }
-}
+  GMPThread* thread = mThread;
+  thread->Join();
 
-ClearKeyDecryptor::~ClearKeyDecryptor()
-{
-  CK_LOGD("ClearKeyDecryptor dtor; key ID = %08x...", *(uint32_t*)&mKey[0]);
-}
-
-uint32_t
-ClearKeyDecryptor::AddRef()
-{
-  return ++mRefCnt;
+  Shutdown();
+  mDecryptionManager = nullptr;
+  Release();
 }
-
-uint32_t
-ClearKeyDecryptor::Release()
-{
-  uint32_t newCount = --mRefCnt;
-  if (!newCount) {
-    if (mThread) {
-      // Shutdown mThread. We cache a pointer to mThread, as the DestroyTask
-      // may run and delete |this| before Post() returns.
-      GMPThread* thread = mThread;
-      thread->Post(new DestroyTask(this));
-      thread->Join();
-    } else {
-      delete this;
-    }
-  }
-
-  return newCount;
-}
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.h
@@ -0,0 +1,88 @@
+/* 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/. */
+
+#ifndef __ClearKeyDecryptor_h__
+#define __ClearKeyDecryptor_h__
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "ClearKeyDecryptionManager.h"
+#include "ClearKeySession.h"
+#include "ClearKeyUtils.h"
+#include "gmp-api/gmp-decryption.h"
+#include "mozilla/RefPtr.h"
+#include "ScopedNSSTypes.h"
+#include "RefCounted.h"
+
+class ClearKeySessionManager MOZ_FINAL : public GMPDecryptor
+                                       , public RefCounted
+{
+public:
+  ClearKeySessionManager();
+
+  virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
+
+  virtual void CreateSession(uint32_t aCreateSessionToken,
+                             uint32_t aPromiseId,
+                             const char* aInitDataType,
+                             uint32_t aInitDataTypeSize,
+                             const uint8_t* aInitData,
+                             uint32_t aInitDataSize,
+                             GMPSessionType aSessionType) MOZ_OVERRIDE;
+
+  virtual void LoadSession(uint32_t aPromiseId,
+                           const char* aSessionId,
+                           uint32_t aSessionIdLength) MOZ_OVERRIDE;
+
+  virtual void UpdateSession(uint32_t aPromiseId,
+                             const char* aSessionId,
+                             uint32_t aSessionIdLength,
+                             const uint8_t* aResponse,
+                             uint32_t aResponseSize) MOZ_OVERRIDE;
+
+  virtual void CloseSession(uint32_t aPromiseId,
+                            const char* aSessionId,
+                            uint32_t aSessionIdLength) MOZ_OVERRIDE;
+
+  virtual void RemoveSession(uint32_t aPromiseId,
+                             const char* aSessionId,
+                             uint32_t aSessionIdLength) MOZ_OVERRIDE;
+
+  virtual void SetServerCertificate(uint32_t aPromiseId,
+                                    const uint8_t* aServerCert,
+                                    uint32_t aServerCertSize) MOZ_OVERRIDE;
+
+  virtual void Decrypt(GMPBuffer* aBuffer,
+                       GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE;
+
+  virtual void DecryptingComplete() MOZ_OVERRIDE;
+
+  void PersistentSessionDataLoaded(GMPErr aStatus,
+                                   uint32_t aPromiseId,
+                                   const std::string& aSessionId,
+                                   const uint8_t* aKeyData,
+                                   uint32_t aKeyDataSize);
+
+private:
+  ~ClearKeySessionManager();
+
+  void DoDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
+  void Shutdown();
+
+  void ClearInMemorySessionData(ClearKeySession* aSession);
+  void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
+
+  mozilla::RefPtr<ClearKeyDecryptionManager> mDecryptionManager;
+
+  GMPDecryptorCallback* mCallback;
+  GMPThread* mThread;
+
+  std::set<KeyId> mKeyIds;
+  std::map<std::string, ClearKeySession*> mSessions;
+};
+
+#endif // __ClearKeyDecryptor_h__
--- a/media/gmp-clearkey/0.1/RefCounted.h
+++ b/media/gmp-clearkey/0.1/RefCounted.h
@@ -7,22 +7,22 @@
 
 // Note: Not thread safe!
 class RefCounted {
 public:
   void AddRef() {
     ++mRefCount;
   }
 
-  void Release() {
-    if (mRefCount == 1) {
+  uint32_t Release() {
+    uint32_t newCount = --mRefCount;
+    if (!newCount) {
       delete this;
-    } else {
-      --mRefCount;
     }
+    return newCount;
   }
 
 protected:
   RefCounted()
     : mRefCount(0)
   {
   }
   virtual ~RefCounted()
--- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -1,16 +1,16 @@
 /* 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 <stdio.h>
 #include <string.h>
 
-#include "ClearKeyDecryptionManager.h"
+#include "ClearKeySessionManager.h"
 
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-platform.h"
 #include "mozilla/Attributes.h"
 
 static GMPPlatformAPI* sPlatform = nullptr;
 GMPPlatformAPI*
 GetPlatform()
@@ -29,17 +29,17 @@ GMPInit(GMPPlatformAPI* aPlatformAPI)
 
 MOZ_EXPORT GMPErr
 GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
 {
   if (strcmp(aApiName, GMP_API_DECRYPTOR)) {
     return GMPNotImplementedErr;
   }
 
-  *aPluginAPI = new ClearKeyDecryptionManager();
+  *aPluginAPI = new ClearKeySessionManager();
 
   return GMPNoErr;
 }
 
 MOZ_EXPORT GMPErr
 GMPShutdown(void)
 {
   return GMPNoErr;
--- a/media/gmp-clearkey/0.1/moz.build
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -7,16 +7,17 @@
 SharedLibrary('clearkey')
 
 FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
 
 UNIFIED_SOURCES += [
     'ClearKeyDecryptionManager.cpp',
     'ClearKeyPersistence.cpp',
     'ClearKeySession.cpp',
+    'ClearKeySessionManager.cpp',
     'ClearKeyStorage.cpp',
     'ClearKeyUtils.cpp',
     'gmp-clearkey.cpp',
     'openaes/oaes_lib.c',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/media/gmp',