Bug 1111391 - Enable keyMessages to be sent before create/load session promise is resolved. r=jwwang
authorChris Pearce <cpearce@mozilla.com>
Fri, 09 Jan 2015 14:30:07 +1300
changeset 222873 a74e49e232f937764b73b2038cfa90030bacbe3b
parent 222872 bd1bfdfd959e133ca304f2c6cfc73a510039d4c2
child 222874 4cd36b909eef9df1829df8325c42d13f7aa03414
push id10731
push usercbook@mozilla.com
push dateFri, 09 Jan 2015 14:51:37 +0000
treeherderfx-team@e6756043d930 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang
bugs1111391
milestone37.0a1
Bug 1111391 - Enable keyMessages to be sent before create/load session promise is resolved. r=jwwang
dom/media/eme/CDMCallbackProxy.cpp
dom/media/eme/CDMCallbackProxy.h
dom/media/eme/CDMProxy.cpp
dom/media/eme/CDMProxy.h
dom/media/eme/MediaKeySession.cpp
dom/media/eme/MediaKeySession.h
dom/media/eme/MediaKeys.cpp
dom/media/eme/MediaKeys.h
dom/media/gmp-plugin/fake.info
dom/media/gmp-plugin/gmp-test-decryptor.h
dom/media/gmp/GMPDecryptorChild.cpp
dom/media/gmp/GMPDecryptorChild.h
dom/media/gmp/GMPDecryptorParent.cpp
dom/media/gmp/GMPDecryptorParent.h
dom/media/gmp/GMPDecryptorProxy.h
dom/media/gmp/PGMPDecryptor.ipdl
dom/media/gmp/gmp-api/gmp-decryption.h
dom/media/gtest/TestGMPCrossOrigin.cpp
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.cpp
media/gmp-clearkey/0.1/ClearKeySession.h
media/gmp-clearkey/0.1/clearkey.info
--- a/dom/media/eme/CDMCallbackProxy.cpp
+++ b/dom/media/eme/CDMCallbackProxy.cpp
@@ -18,46 +18,46 @@
 namespace mozilla {
 
 CDMCallbackProxy::CDMCallbackProxy(CDMProxy* aProxy)
   : mProxy(aProxy)
 {
 
 }
 
-class NewSessionTask : public nsRunnable {
+class SetSessionIdTask : public nsRunnable {
 public:
-  NewSessionTask(CDMProxy* aProxy,
-                 uint32_t aPromiseId,
-                 const nsCString& aSessionId)
+  SetSessionIdTask(CDMProxy* aProxy,
+                   uint32_t aToken,
+                   const nsCString& aSessionId)
     : mProxy(aProxy)
-    , mPid(aPromiseId)
+    , mToken(aToken)
     , mSid(NS_ConvertUTF8toUTF16(aSessionId))
   {
   }
 
   NS_IMETHOD Run() {
-    mProxy->OnResolveNewSessionPromise(mPid, mSid);
+    mProxy->OnSetSessionId(mToken, mSid);
     return NS_OK;
   }
 
   nsRefPtr<CDMProxy> mProxy;
-  dom::PromiseId mPid;
+  uint32_t mToken;
   nsString mSid;
 };
 
 void
-CDMCallbackProxy::ResolveNewSessionPromise(uint32_t aPromiseId,
-                                           const nsCString& aSessionId)
+CDMCallbackProxy::SetSessionId(uint32_t aToken,
+                               const nsCString& aSessionId)
 {
   MOZ_ASSERT(mProxy->IsOnGMPThread());
 
-  nsRefPtr<nsIRunnable> task(new NewSessionTask(mProxy,
-                                                aPromiseId,
-                                                aSessionId));
+  nsRefPtr<nsIRunnable> task(new SetSessionIdTask(mProxy,
+                                                  aToken,
+                                                  aSessionId));
   NS_DispatchToMainThread(task);
 }
 
 class LoadSessionTask : public nsRunnable {
 public:
   LoadSessionTask(CDMProxy* aProxy,
                   uint32_t aPromiseId,
                   bool aSuccess)
--- a/dom/media/eme/CDMCallbackProxy.h
+++ b/dom/media/eme/CDMCallbackProxy.h
@@ -12,18 +12,18 @@
 #include "GMPDecryptorProxy.h"
 
 namespace mozilla {
 
 // Proxies call backs from the CDM on the GMP thread back to the MediaKeys
 // object on the main thread.
 class CDMCallbackProxy : public GMPDecryptorProxyCallback {
 public:
-  virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
-                                        const nsCString& aSessionId) MOZ_OVERRIDE;
+  virtual void SetSessionId(uint32_t aCreateSessionToken,
+                            const nsCString& aSessionId) MOZ_OVERRIDE;
 
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccess) MOZ_OVERRIDE;
 
   virtual void ResolvePromise(uint32_t aPromiseId) MOZ_OVERRIDE;
 
   virtual void RejectPromise(uint32_t aPromiseId,
                              nsresult aException,
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -133,26 +133,28 @@ CDMProxy::OnCDMCreated(uint32_t aPromise
   if (mKeys.IsNull()) {
     return;
   }
   MOZ_ASSERT(!GetNodeId().IsEmpty());
   mKeys->OnCDMCreated(aPromiseId, GetNodeId());
 }
 
 void
-CDMProxy::CreateSession(dom::SessionType aSessionType,
+CDMProxy::CreateSession(uint32_t aCreateSessionToken,
+                        dom::SessionType aSessionType,
                         PromiseId aPromiseId,
                         const nsAString& aInitDataType,
                         nsTArray<uint8_t>& aInitData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mGMPThread);
 
   nsAutoPtr<CreateSessionData> data(new CreateSessionData());
   data->mSessionType = aSessionType;
+  data->mCreateSessionToken = aCreateSessionToken;
   data->mPromiseId = aPromiseId;
   data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
   data->mInitData = Move(aInitData);
 
   nsRefPtr<nsIRunnable> task(
     NS_NewRunnableMethodWithArg<nsAutoPtr<CreateSessionData>>(this, &CDMProxy::gmp_CreateSession, data));
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
@@ -169,17 +171,18 @@ ToGMPSessionType(dom::SessionType aSessi
 void
 CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  mCDM->CreateSession(aData->mPromiseId,
+  mCDM->CreateSession(aData->mCreateSessionToken,
+                      aData->mPromiseId,
                       aData->mInitDataType,
                       aData->mInitData,
                       ToGMPSessionType(aData->mSessionType));
 }
 
 void
 CDMProxy::LoadSession(PromiseId aPromiseId,
                       const nsAString& aSessionId)
@@ -379,24 +382,26 @@ CDMProxy::ResolvePromise(PromiseId aId)
 
 const nsCString&
 CDMProxy::GetNodeId() const
 {
   return mNodeId;
 }
 
 void
-CDMProxy::OnResolveNewSessionPromise(uint32_t aPromiseId,
-                                     const nsAString& aSessionId)
+CDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
+                         const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
-  mKeys->OnSessionCreated(aPromiseId, aSessionId);
+
+  nsRefPtr<dom::MediaKeySession> session(mKeys->GetPendingSession(aCreateSessionToken));
+  session->SetSessionId(aSessionId);
 }
 
 void
 CDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -52,17 +52,18 @@ public:
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
             bool aInPrivateBrowsing);
 
   // Main thread only.
   // Uses the CDM to create a key session.
   // Calls MediaKeys::OnSessionActivated() when session is created.
   // Assumes ownership of (Move()s) aInitData's contents.
-  void CreateSession(dom::SessionType aSessionType,
+  void CreateSession(uint32_t aCreateSessionToken,
+                     dom::SessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
                      nsTArray<uint8_t>& aInitData);
 
   // Main thread only.
   // Uses the CDM to load a presistent session stored on disk.
   // Calls MediaKeys::OnSessionActivated() when session is loaded.
   void LoadSession(PromiseId aPromiseId,
@@ -106,18 +107,18 @@ public:
 
   // Main thread only.
   void Terminated();
 
   // Threadsafe.
   const nsCString& GetNodeId() const;
 
   // Main thread only.
-  void OnResolveNewSessionPromise(uint32_t aPromiseId,
-                                  const nsAString& aSessionId);
+  void OnSetSessionId(uint32_t aCreateSessionToken,
+                      const nsAString& aSessionId);
 
   // Main thread only.
   void OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess);
 
   // Main thread only.
   void OnSessionMessage(const nsAString& aSessionId,
                         nsTArray<uint8_t>& aMessage,
                         const nsAString& aDestinationURL);
@@ -184,16 +185,17 @@ private:
   // GMP thread only.
   void gmp_Shutdown();
 
   // Main thread only.
   void OnCDMCreated(uint32_t aPromiseId);
 
   struct CreateSessionData {
     dom::SessionType mSessionType;
+    uint32_t mCreateSessionToken;
     PromiseId mPromiseId;
     nsAutoCString mInitDataType;
     nsTArray<uint8_t> mInitData;
   };
   // GMP thread only.
   void gmp_CreateSession(nsAutoPtr<CreateSessionData> aData);
 
   struct SessionOpData {
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -25,35 +25,44 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Media
                                    mClosed)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaKeySession)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MediaKeySession, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaKeySession, DOMEventTargetHelper)
 
+// Count of number of instances. Used to give each instance a
+// unique token.
+static uint32_t sMediaKeySessionNum = 0;
+
 MediaKeySession::MediaKeySession(nsPIDOMWindow* aParent,
                                  MediaKeys* aKeys,
                                  const nsAString& aKeySystem,
                                  SessionType aSessionType,
                                  ErrorResult& aRv)
   : DOMEventTargetHelper(aParent)
   , mKeys(aKeys)
   , mKeySystem(aKeySystem)
   , mSessionType(aSessionType)
+  , mToken(sMediaKeySessionNum++)
   , mIsClosed(false)
   , mUninitialized(true)
 {
   MOZ_ASSERT(aParent);
   mClosed = mKeys->MakePromise(aRv);
 }
 
-void MediaKeySession::Init(const nsAString& aSessionId)
+void MediaKeySession::SetSessionId(const nsAString& aSessionId)
 {
+  if (NS_WARN_IF(!mSessionId.IsEmpty())) {
+    return;
+  }
   mSessionId = aSessionId;
+  mKeys->OnSessionIdReady(this);
 }
 
 MediaKeySession::~MediaKeySession()
 {
 }
 
 MediaKeyError*
 MediaKeySession::GetError() const
@@ -117,19 +126,18 @@ MediaKeySession::GenerateRequest(const n
   nsTArray<uint8_t> data;
   if (aInitDataType.IsEmpty() ||
       !CopyArrayBufferViewOrArrayBufferData(aInitData, data)) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return promise.forget();
   }
 
   PromiseId pid = mKeys->StorePromise(promise);
-  mKeys->OnSessionPending(pid, this);
-
-  mKeys->GetCDMProxy()->CreateSession(mSessionType,
+  mKeys->GetCDMProxy()->CreateSession(Token(),
+                                      mSessionType,
                                       pid,
                                       aInitDataType, data);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
@@ -147,21 +155,25 @@ MediaKeySession::Load(const nsAString& a
 
   if (!mUninitialized) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return promise.forget();
   }
 
   mUninitialized = false;
 
-  Init(aSessionId);
-  auto pid = mKeys->StorePromise(promise);
-  mKeys->OnSessionPending(pid, this);
+  // We now know the sessionId being loaded into this session. Remove the
+  // session from its owning MediaKey's set of sessions awaiting a sessionId.
+  nsRefPtr<MediaKeySession> session(mKeys->GetPendingSession(Token()));
+  MOZ_ASSERT(session == this, "Session should be awaiting id on its own token");
 
-  mKeys->GetCDMProxy()->LoadSession(pid, aSessionId);
+  // Associate with the known sessionId.
+  SetSessionId(aSessionId);
+
+  mKeys->GetCDMProxy()->LoadSession(mKeys->StorePromise(promise), aSessionId);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResult& aRv)
 {
   nsRefPtr<Promise> promise(mKeys->MakePromise(aRv));
@@ -290,10 +302,16 @@ MediaKeySession::DispatchKeysChange()
   if (IsClosed()) {
     return;
   }
   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(this, NS_LITERAL_STRING("keyschange"), false);
   asyncDispatcher->PostDOMEvent();
 }
 
+uint32_t
+MediaKeySession::Token() const
+{
+  return mToken;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeySession.h
+++ b/dom/media/eme/MediaKeySession.h
@@ -38,17 +38,17 @@ public:
                                            DOMEventTargetHelper)
 public:
   MediaKeySession(nsPIDOMWindow* aParent,
                   MediaKeys* aKeys,
                   const nsAString& aKeySystem,
                   SessionType aSessionType,
                   ErrorResult& aRv);
 
-  void Init(const nsAString& aSessionId);
+  void SetSessionId(const nsAString& aSessionId);
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // Mark this as resultNotAddRefed to return raw pointers
   MediaKeyError* GetError() const;
 
   void GetKeySystem(nsString& aRetval) const;
 
@@ -85,26 +85,30 @@ public:
   void DispatchKeyError(uint32_t system_code);
 
   void DispatchKeysChange();
 
   void OnClosed();
 
   bool IsClosed() const;
 
+  // Process-unique identifier.
+  uint32_t Token() const;
+
 private:
   ~MediaKeySession();
 
   nsRefPtr<Promise> mClosed;
 
   nsRefPtr<MediaKeyError> mMediaKeyError;
   nsRefPtr<MediaKeys> mKeys;
   const nsString mKeySystem;
   nsString mSessionId;
   const SessionType mSessionType;
+  const uint32_t mToken;
   bool mIsClosed;
   bool mUninitialized;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -203,16 +203,38 @@ MediaKeys::RejectPromise(PromiseId aId, 
 
   if (mCreatePromiseId == aId) {
     // Note: This will probably destroy the MediaKeys object!
     Release();
   }
 }
 
 void
+MediaKeys::OnSessionIdReady(MediaKeySession* aSession)
+{
+  if (!aSession) {
+    NS_WARNING("Invalid MediaKeySession passed to OnSessionIdReady()");
+    return;
+  }
+  if (mKeySessions.Contains(aSession->GetSessionId())) {
+    NS_WARNING("MediaKeySession's made ready multiple times!");
+    return;
+  }
+  if (mPendingSessions.Contains(aSession->Token())) {
+    NS_WARNING("MediaKeySession made ready when it wasn't waiting to be ready!");
+    return;
+  }
+  if (aSession->GetSessionId().IsEmpty()) {
+    NS_WARNING("MediaKeySession with invalid sessionId passed to OnSessionIdReady()");
+    return;
+  }
+  mKeySessions.Put(aSession->GetSessionId(), aSession);
+}
+
+void
 MediaKeys::ResolvePromise(PromiseId aId)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     NS_WARNING("MediaKeys tried to resolve a non-existent promise");
     return;
   }
   if (mPendingSessions.Contains(aId)) {
@@ -341,80 +363,30 @@ MediaKeys::CreateSession(SessionType aSe
                          ErrorResult& aRv)
 {
   nsRefPtr<MediaKeySession> session = new MediaKeySession(GetParentObject(),
                                                           this,
                                                           mKeySystem,
                                                           aSessionType,
                                                           aRv);
 
-  return session.forget();
-}
-
-void
-MediaKeys::OnSessionPending(PromiseId aId, MediaKeySession* aSession)
-{
-  MOZ_ASSERT(mPromises.Contains(aId));
-  MOZ_ASSERT(!mPendingSessions.Contains(aId));
-  mPendingSessions.Put(aId, aSession);
-}
+  // Add session to the set of sessions awaiting their sessionId being ready.
+  mPendingSessions.Put(session->Token(), session);
 
-void
-MediaKeys::OnSessionCreated(PromiseId aId, const nsAString& aSessionId)
-{
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
-  if (!promise) {
-    NS_WARNING("MediaKeys tried to resolve a non-existent promise");
-    return;
-  }
-  MOZ_ASSERT(mPendingSessions.Contains(aId));
-
-  nsRefPtr<MediaKeySession> session;
-  bool gotSession = mPendingSessions.Get(aId, getter_AddRefs(session));
-  // Session has completed creation/loading, remove it from mPendingSessions,
-  // and resolve the promise with it. We store it in mKeySessions, so we can
-  // find it again if we need to send messages to it etc.
-  mPendingSessions.Remove(aId);
-  if (!gotSession || !session) {
-    NS_WARNING("Received activation for non-existent session!");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-
-  session->Init(aSessionId);
-  mKeySessions.Put(aSessionId, session);
-  promise->MaybeResolve(session);
+  return session.forget();
 }
 
 void
 MediaKeys::OnSessionLoaded(PromiseId aId, bool aSuccess)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     NS_WARNING("MediaKeys tried to resolve a non-existent promise");
     return;
   }
-  MOZ_ASSERT(mPendingSessions.Contains(aId));
-
-  nsRefPtr<MediaKeySession> session;
-  bool gotSession = mPendingSessions.Get(aId, getter_AddRefs(session));
-  // Session has completed creation/loading, remove it from mPendingSessions,
-  // and resolve the promise with it. We store it in mKeySessions, so we can
-  // find it again if we need to send messages to it etc.
-  mPendingSessions.Remove(aId);
-  if (!gotSession || !session) {
-    NS_WARNING("Received activation for non-existent session!");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-
-  MOZ_ASSERT(!session->GetSessionId().IsEmpty() &&
-             !mKeySessions.Contains(session->GetSessionId()));
-
-  mKeySessions.Put(session->GetSessionId(), session);
   promise->MaybeResolve(aSuccess);
 }
 
 void
 MediaKeys::OnSessionClosed(MediaKeySession* aSession)
 {
   nsAutoString id;
   aSession->GetSessionId(id);
@@ -424,16 +396,25 @@ MediaKeys::OnSessionClosed(MediaKeySessi
 already_AddRefed<MediaKeySession>
 MediaKeys::GetSession(const nsAString& aSessionId)
 {
   nsRefPtr<MediaKeySession> session;
   mKeySessions.Get(aSessionId, getter_AddRefs(session));
   return session.forget();
 }
 
+already_AddRefed<MediaKeySession>
+MediaKeys::GetPendingSession(uint32_t aToken)
+{
+  nsRefPtr<MediaKeySession> session;
+  mPendingSessions.Get(aToken, getter_AddRefs(session));
+  mPendingSessions.Remove(aToken);
+  return session.forget();
+}
+
 const nsCString&
 MediaKeys::GetNodeId() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mNodeId;
 }
 
 bool
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -69,25 +69,31 @@ public:
                                                   ErrorResult& aRv);
 
   // JavaScript: MediaKeys.SetServerCertificate()
   already_AddRefed<Promise> SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
                                                  ErrorResult& aRv);
 
   already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
 
-  // Called once a Create() operation succeeds.
+  // Removes and returns MediaKeySession from the set of sessions awaiting
+  // their sessionId to be assigned.
+  already_AddRefed<MediaKeySession> GetPendingSession(uint32_t aToken);
+
+  // Called once a Init() operation succeeds.
   void OnCDMCreated(PromiseId aId, const nsACString& aNodeId);
-  // Called when GenerateRequest or Load have been called on a MediaKeySession
-  // and we are waiting for its initialisation to finish.
-  void OnSessionPending(PromiseId aId, MediaKeySession* aSession);
-  // Called once a CreateSession succeeds.
-  void OnSessionCreated(PromiseId aId, const nsAString& aSessionId);
+
+  // Called once the CDM generates a sessionId while servicing a
+  // MediaKeySession.generateRequest() or MediaKeySession.load() call,
+  // once the sessionId of a MediaKeySession is known.
+  void OnSessionIdReady(MediaKeySession* aSession);
+
   // Called once a LoadSession succeeds.
   void OnSessionLoaded(PromiseId aId, bool aSuccess);
+
   // Called once a session has closed.
   void OnSessionClosed(MediaKeySession* aSession);
 
   CDMProxy* GetCDMProxy() { return mProxy; }
 
   // Makes a new promise, or nullptr on failure.
   already_AddRefed<Promise> MakePromise(ErrorResult& aRv);
   // Stores promise in mPromises, returning an ID that can be used to retrieve
--- a/dom/media/gmp-plugin/fake.info
+++ b/dom/media/gmp-plugin/fake.info
@@ -1,5 +1,5 @@
 Name: fake
 Description: Fake GMP Plugin
 Version: 1.0
-APIs: encode-video[h264], decode-video[h264], eme-decrypt[fake]
+APIs: encode-video[h264], decode-video[h264], eme-decrypt-v2[fake]
 Libraries: dxva2.dll
--- a/dom/media/gmp-plugin/gmp-test-decryptor.h
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.h
@@ -15,17 +15,18 @@ class FakeDecryptor : public GMPDecrypto
 public:
 
   explicit FakeDecryptor(GMPDecryptorHost* aHost);
 
   virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE {
     mCallback = aCallback;
   }
 
-  virtual void CreateSession(uint32_t aPromiseId,
+  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
   {
   }
 
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -44,22 +44,22 @@ GMPDecryptorChild::~GMPDecryptorChild()
 void
 GMPDecryptorChild::Init(GMPDecryptor* aSession)
 {
   MOZ_ASSERT(aSession);
   mSession = aSession;
 }
 
 void
-GMPDecryptorChild::ResolveNewSessionPromise(uint32_t aPromiseId,
-                                            const char* aSessionId,
-                                            uint32_t aSessionIdLength)
+GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken,
+                                const char* aSessionId,
+                                uint32_t aSessionIdLength)
 {
-  CALL_ON_GMP_THREAD(SendResolveNewSessionPromise,
-                     aPromiseId, nsAutoCString(aSessionId, aSessionIdLength));
+  CALL_ON_GMP_THREAD(SendSetSessionId,
+                     aCreateSessionToken, nsAutoCString(aSessionId, aSessionIdLength));
 }
 
 void
 GMPDecryptorChild::ResolveLoadSessionPromise(uint32_t aPromiseId,
                                              bool aSuccess)
 {
   CALL_ON_GMP_THREAD(SendResolveLoadSessionPromise, aPromiseId, aSuccess);
 }
@@ -204,26 +204,28 @@ GMPDecryptorChild::RecvInit()
   if (!mSession) {
     return false;
   }
   mSession->Init(this);
   return true;
 }
 
 bool
-GMPDecryptorChild::RecvCreateSession(const uint32_t& aPromiseId,
+GMPDecryptorChild::RecvCreateSession(const uint32_t& aCreateSessionToken,
+                                     const uint32_t& aPromiseId,
                                      const nsCString& aInitDataType,
                                      const nsTArray<uint8_t>& aInitData,
                                      const GMPSessionType& aSessionType)
 {
   if (!mSession) {
     return false;
   }
 
-  mSession->CreateSession(aPromiseId,
+  mSession->CreateSession(aCreateSessionToken,
+                          aPromiseId,
                           aInitDataType.get(),
                           aInitDataType.Length(),
                           aInitData.Elements(),
                           aInitData.Length(),
                           aSessionType);
 
   return true;
 }
--- a/dom/media/gmp/GMPDecryptorChild.h
+++ b/dom/media/gmp/GMPDecryptorChild.h
@@ -26,19 +26,19 @@ public:
 
   explicit GMPDecryptorChild(GMPChild* aPlugin,
                              const nsTArray<uint8_t>& aPluginVoucher,
                              const nsTArray<uint8_t>& aSandboxVoucher);
 
   void Init(GMPDecryptor* aSession);
 
   // GMPDecryptorCallback
-  virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
-                                        const char* aSessionId,
-                                        uint32_t aSessionIdLength) MOZ_OVERRIDE;
+  virtual void SetSessionId(uint32_t aCreateSessionToken,
+                            const char* aSessionId,
+                            uint32_t aSessionIdLength) MOZ_OVERRIDE;
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccess) MOZ_OVERRIDE;
   virtual void ResolvePromise(uint32_t aPromiseId) MOZ_OVERRIDE;
 
   virtual void RejectPromise(uint32_t aPromiseId,
                              GMPDOMException aException,
                              const char* aMessage,
                              uint32_t aMessageLength) MOZ_OVERRIDE;
@@ -85,17 +85,18 @@ public:
   virtual void GetPluginVoucher(const uint8_t** aVoucher,
                                 uint32_t* aVoucherLength) MOZ_OVERRIDE;
 private:
   ~GMPDecryptorChild();
 
   // GMPDecryptorChild
   virtual bool RecvInit() MOZ_OVERRIDE;
 
-  virtual bool RecvCreateSession(const uint32_t& aPromiseId,
+  virtual bool RecvCreateSession(const uint32_t& aCreateSessionToken,
+                                 const uint32_t& aPromiseId,
                                  const nsCString& aInitDataType,
                                  const nsTArray<uint8_t>& aInitData,
                                  const GMPSessionType& aSessionType) MOZ_OVERRIDE;
 
   virtual bool RecvLoadSession(const uint32_t& aPromiseId,
                                const nsCString& aSessionId) MOZ_OVERRIDE;
 
   virtual bool RecvUpdateSession(const uint32_t& aPromiseId,
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -38,28 +38,29 @@ GMPDecryptorParent::Init(GMPDecryptorPro
   if (!SendInit()) {
     return NS_ERROR_FAILURE;
   }
   mIsOpen = true;
   return NS_OK;
 }
 
 void
-GMPDecryptorParent::CreateSession(uint32_t aPromiseId,
+GMPDecryptorParent::CreateSession(uint32_t aCreateSessionToken,
+                                  uint32_t aPromiseId,
                                   const nsCString& aInitDataType,
                                   const nsTArray<uint8_t>& aInitData,
                                   GMPSessionType aSessionType)
 {
   if (!mIsOpen) {
     NS_WARNING("Trying to use a dead GMP decrypter!");
     return;
   }
   // Caller should ensure parameters passed in from JS are valid.
   MOZ_ASSERT(!aInitDataType.IsEmpty() && !aInitData.IsEmpty());
-  unused << SendCreateSession(aPromiseId, aInitDataType, aInitData, aSessionType);
+  unused << SendCreateSession(aCreateSessionToken, aPromiseId, aInitDataType, aInitData, aSessionType);
 }
 
 void
 GMPDecryptorParent::LoadSession(uint32_t aPromiseId,
                                 const nsCString& aSessionId)
 {
   if (!mIsOpen) {
     NS_WARNING("Trying to use a dead GMP decrypter!");
@@ -140,24 +141,24 @@ GMPDecryptorParent::Decrypt(uint32_t aId
                          aCrypto.iv,
                          aCrypto.plain_sizes,
                          aCrypto.encrypted_sizes);
 
   unused << SendDecrypt(aId, aBuffer, data);
 }
 
 bool
-GMPDecryptorParent::RecvResolveNewSessionPromise(const uint32_t& aPromiseId,
-                                                 const nsCString& aSessionId)
+GMPDecryptorParent::RecvSetSessionId(const uint32_t& aCreateSessionId,
+                                     const nsCString& aSessionId)
 {
   if (!mIsOpen) {
     NS_WARNING("Trying to use a dead GMP decrypter!");
     return false;
   }
-  mCallback->ResolveNewSessionPromise(aPromiseId, aSessionId);
+  mCallback->SetSessionId(aCreateSessionId, aSessionId);
   return true;
 }
 
 bool
 GMPDecryptorParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
                                                   const bool& aSuccess)
 {
   if (!mIsOpen) {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -25,17 +25,18 @@ class GMPDecryptorParent MOZ_FINAL : pub
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPDecryptorParent)
 
   explicit GMPDecryptorParent(GMPParent *aPlugin);
 
   // GMPDecryptorProxy
   virtual nsresult Init(GMPDecryptorProxyCallback* aCallback) MOZ_OVERRIDE;
 
-  virtual void CreateSession(uint32_t aPromiseId,
+  virtual void CreateSession(uint32_t aCreateSessionToken,
+                             uint32_t aPromiseId,
                              const nsCString& aInitDataType,
                              const nsTArray<uint8_t>& aInitData,
                              GMPSessionType aSessionType) MOZ_OVERRIDE;
 
   virtual void LoadSession(uint32_t aPromiseId,
                            const nsCString& aSessionId) MOZ_OVERRIDE;
 
   virtual void UpdateSession(uint32_t aPromiseId,
@@ -59,18 +60,18 @@ public:
 
   void Shutdown();
 
 private:
   ~GMPDecryptorParent();
 
   // PGMPDecryptorParent
 
-  virtual bool RecvResolveNewSessionPromise(const uint32_t& aPromiseId,
-                                            const nsCString& aSessionId) MOZ_OVERRIDE;
+  virtual bool RecvSetSessionId(const uint32_t& aCreateSessionToken,
+                                const nsCString& aSessionId) MOZ_OVERRIDE;
 
   virtual bool RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
                                              const bool& aSuccess) MOZ_OVERRIDE;
 
   virtual bool RecvResolvePromise(const uint32_t& aPromiseId) MOZ_OVERRIDE;
 
   virtual bool RecvRejectPromise(const uint32_t& aPromiseId,
                                  const GMPDOMException& aException,
--- a/dom/media/gmp/GMPDecryptorProxy.h
+++ b/dom/media/gmp/GMPDecryptorProxy.h
@@ -13,18 +13,18 @@
 namespace mp4_demuxer {
 class CryptoSample;
 }
 
 class GMPDecryptorProxyCallback : public GMPCallbackBase {
 public:
   ~GMPDecryptorProxyCallback() {}
 
-  virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
-                                        const nsCString& aSessionId) = 0;
+  virtual void SetSessionId(uint32_t aCreateSessionId,
+                            const nsCString& aSessionId) = 0;
 
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccess) = 0;
 
   virtual void ResolvePromise(uint32_t aPromiseId) = 0;
 
   virtual void RejectPromise(uint32_t aPromiseId,
                              nsresult aException,
@@ -58,17 +58,18 @@ public:
 };
 
 class GMPDecryptorProxy {
 public:
   ~GMPDecryptorProxy() {}
 
   virtual nsresult Init(GMPDecryptorProxyCallback* aCallback) = 0;
 
-  virtual void CreateSession(uint32_t aPromiseId,
+  virtual void CreateSession(uint32_t aCreateSessionToken,
+                             uint32_t aPromiseId,
                              const nsCString& aInitDataType,
                              const nsTArray<uint8_t>& aInitData,
                              GMPSessionType aSessionType) = 0;
 
   virtual void LoadSession(uint32_t aPromiseId,
                            const nsCString& aSessionId) = 0;
 
   virtual void UpdateSession(uint32_t aPromiseId,
--- a/dom/media/gmp/PGMPDecryptor.ipdl
+++ b/dom/media/gmp/PGMPDecryptor.ipdl
@@ -15,17 +15,18 @@ namespace gmp {
 
 async protocol PGMPDecryptor
 {
   manager PGMP;
 child:
 
   Init();
 
-  CreateSession(uint32_t aPromiseId,
+  CreateSession(uint32_t aCreateSessionToken,
+                uint32_t aPromiseId,
                 nsCString aInitDataType,
                 uint8_t[] aInitData,
                 GMPSessionType aSessionType);
 
   LoadSession(uint32_t aPromiseId,
               nsCString aSessionId);
 
   UpdateSession(uint32_t aPromiseId,
@@ -45,18 +46,18 @@ child:
           uint8_t[] aBuffer,
           GMPDecryptionData aMetadata);
 
   DecryptingComplete();
 
 parent:
   __delete__();
 
-  ResolveNewSessionPromise(uint32_t aPromiseId,
-                           nsCString aSessionId);
+  SetSessionId(uint32_t aCreateSessionToken,
+               nsCString aSessionId);
 
   ResolveLoadSessionPromise(uint32_t aPromiseId,
                             bool aSuccess);
 
   ResolvePromise(uint32_t aPromiseId);
 
   RejectPromise(uint32_t aPromiseId,
                 GMPDOMException aDOMExceptionCode,
--- a/dom/media/gmp/gmp-api/gmp-decryption.h
+++ b/dom/media/gmp/gmp-api/gmp-decryption.h
@@ -91,23 +91,35 @@ typedef int64_t GMPTimestamp;
 // Capability; CDM can decrypt and then decode encrypted buffers,
 // and return decompressed samples to Gecko for playback.
 #define GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO (uint64_t(1) << 2)
 #define GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO (uint64_t(1) << 3)
 
 // Callbacks to be called from the CDM. Threadsafe.
 class GMPDecryptorCallback {
 public:
-  // Resolves a promise for a session created.
-  // Passes the session id to be exposed to JavaScript.
-  // Must be called before SessionMessage().
+
+  // The GMPDecryptor should call this in response to a call to
+  // GMPDecryptor::CreateSession(). The GMP host calls CreateSession() when
+  // MediaKeySession.generateRequest() is called by JavaScript.
+  // After CreateSession() is called, the GMPDecryptor should call
+  // GMPDecryptorCallback::SetSessionId() to set the sessionId exposed to
+  // JavaScript on the MediaKeySession on which the generateRequest() was
+  // called. SetSessionId() must be called before
+  // GMPDecryptorCallback::SessionMessage() will work.
   // aSessionId must be null terminated.
-  virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
-                                        const char* aSessionId,
-                                        uint32_t aSessionIdLength) = 0;
+  // Note: pass the aCreateSessionToken from the CreateSession() call,
+  // and then once the session has sent any messages required for the
+  // license request to be sent, then resolve the aPromiseId that was passed
+  // to GMPDecryptor::CreateSession().
+  // Note: GMPDecryptor::LoadSession() does *not* need to call SetSessionId()
+  // for GMPDecryptorCallback::SessionMessage() to work.
+  virtual void SetSessionId(uint32_t aCreateSessionToken,
+                            const char* aSessionId,
+                            uint32_t aSessionIdLength) = 0;
 
   // Resolves a promise for a session loaded.
   // Resolves to false if we don't have any session data stored for the given
   // session ID.
   // Must be called before SessionMessage().
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccess) = 0;
 
@@ -117,17 +129,17 @@ public:
   // Called to reject a promise with a DOMException.
   // aMessage is logged to the WebConsole.
   // aMessage is optional, but if present must be null terminated.
   virtual void RejectPromise(uint32_t aPromiseId,
                              GMPDOMException aException,
                              const char* aMessage,
                              uint32_t aMessageLength) = 0;
 
-  // Called by the CDM when it has a message for session |session_id|.
+  // Called by the CDM when it has a message for a session.
   // Length parameters should not include null termination.
   // aSessionId must be null terminated.
   virtual void SessionMessage(const char* aSessionId,
                               uint32_t aSessionIdLength,
                               const uint8_t* aMessage,
                               uint32_t aMessageLength,
                               const char* aDestinationURL,
                               uint32_t aDestinationURLLength) = 0;
@@ -196,69 +208,111 @@ public:
 };
 
 enum GMPSessionType {
   kGMPTemporySession = 0,
   kGMPPersistentSession = 1,
   kGMPSessionInvalid = 2 // Must always be last.
 };
 
-#define GMP_API_DECRYPTOR "eme-decrypt"
+#define GMP_API_DECRYPTOR "eme-decrypt-v2"
 
 // API exposed by plugin library to manage decryption sessions.
 // When the Host requests this by calling GMPGetAPIFunc().
 //
 // API name macro: GMP_API_DECRYPTOR
 // Host API: GMPDecryptorHost
 class GMPDecryptor {
 public:
 
   // Sets the callback to use with the decryptor to return results
   // to Gecko.
-  virtual void Init(GMPDecryptorCallback* aCallback) = 0;
-
-  // Requests the creation of a session given |aType| and |aInitData|.
-  // Decryptor should callback GMPDecryptorCallback::SessionCreated()
-  // with the web session ID on success, or SessionError() on failure,
-  // and then call KeyIdUsable() as keys for that session become
-  // usable.
   //
   // The CDM must also call GMPDecryptorCallback::SetCapabilities()
   // exactly once during start up, to inform Gecko whether to use the CDM
   // in decrypt or decrypt-and-decode mode.
-  virtual void CreateSession(uint32_t aPromiseId,
+  //
+  // Note: GMPDecryptorCallback::SetCapabilities() must be called before
+  // Gecko will send any samples for decryption to the GMP.
+  virtual void Init(GMPDecryptorCallback* aCallback) = 0;
+
+  // Initiates the creation of a session given |aType| and |aInitData|, and
+  // the generation of a license request message.
+  //
+  // This corresponds to a MediaKeySession.generateRequest() call in JS.
+  //
+  // The GMPDecryptor must do the following, in order, upon this method
+  // being called:
+  //
+  // 1. Generate a sessionId to expose to JS, and call
+  //    GMPDecryptorCallback::SetSessionId(aCreateSessionToken, sessionId...)
+  //    with the sessionId to be exposed to JS/EME on the MediaKeySession
+  //    object on which generateRequest() was called, and then
+  // 2. send any messages to JS/EME required to generate a license request
+  //    given the supplied initData, and then
+  // 3. generate a license request message, and send it to JS/EME, and then
+  // 4. call GMPDecryptorCallback::ResolvePromise().
+  //
+  // Note: GMPDecryptorCallback::SetSessionId(aCreateSessionToken, sessionId, ...)
+  // *must* be called before GMPDecryptorCallback::SendMessage(sessionId, ...)
+  // will work.
+  //
+  // If generating the request fails, reject aPromiseId by calling
+  // GMPDecryptorCallback::RejectPromise().
+  virtual void CreateSession(uint32_t aCreateSessionToken,
+                             uint32_t aPromiseId,
                              const char* aInitDataType,
                              uint32_t aInitDataTypeSize,
                              const uint8_t* aInitData,
                              uint32_t aInitDataSize,
                              GMPSessionType aSessionType) = 0;
 
   // Loads a previously loaded persistent session.
+  //
+  // This corresponds to a MediaKeySession.load() call in JS.
+  //
+  // The GMPDecryptor must do the following, in order, upon this method
+  // being called:
+  //
+  // 1. Send any messages to JS/EME, or read from storage, whatever is
+  //    required to load the session, and then
+  // 2. if there is no session with the given sessionId loadable, call
+  //    ResolveLoadSessionPromise(aPromiseId, false), otherwise
+  // 2. mark the session's keys as usable, and then
+  // 3. update the session's expiration, and then
+  // 4. call GMPDecryptorCallback::ResolveLoadSessionPromise(aPromiseId, true).
+  //
+  // If loading the session fails due to error, reject aPromiseId by calling
+  // GMPDecryptorCallback::RejectPromise().
   virtual void LoadSession(uint32_t aPromiseId,
                            const char* aSessionId,
                            uint32_t aSessionIdLength) = 0;
 
   // Updates the session with |aResponse|.
+  // This corresponds to a MediaKeySession.update() call in JS.
   virtual void UpdateSession(uint32_t aPromiseId,
                              const char* aSessionId,
                              uint32_t aSessionIdLength,
                              const uint8_t* aResponse,
                              uint32_t aResponseSize) = 0;
 
   // Releases the resources (keys) for the specified session.
+  // This corresponds to a MediaKeySession.close() call in JS.
   virtual void CloseSession(uint32_t aPromiseId,
                             const char* aSessionId,
                             uint32_t aSessionIdLength) = 0;
 
   // Removes the resources (keys) for the specified session.
+  // This corresponds to a MediaKeySession.remove() call in JS.
   virtual void RemoveSession(uint32_t aPromiseId,
                              const char* aSessionId,
                              uint32_t aSessionIdLength) = 0;
 
   // Resolve/reject promise on completion.
+  // This corresponds to a MediaKeySession.setServerCertificate() call in JS.
   virtual void SetServerCertificate(uint32_t aPromiseId,
                                     const uint8_t* aServerCert,
                                     uint32_t aServerCertSize) = 0;
 
   // Asynchronously decrypts aBuffer in place. When the decryption is
   // complete, GMPDecryptor should write the decrypted data back into the
   // same GMPBuffer object and return it to Gecko by calling Decrypted(),
   // with the GMPNoErr successcode. If decryption fails, call Decrypted()
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -905,18 +905,18 @@ class GMPStorageTest : public GMPDecrypt
       nsRefPtr<nsIRunnable> continuation = mExpected[0].mContinuation;
       mExpected.RemoveElementAt(0);
       if (continuation) {
         NS_DispatchToCurrentThread(continuation);
       }
     }
   }
 
-  virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
-                                        const nsCString& aSessionId) MOZ_OVERRIDE { }
+  virtual void SetSessionId(uint32_t aCreateSessionToken,
+                            const nsCString& aSessionId) MOZ_OVERRIDE { }
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccess) MOZ_OVERRIDE {}
   virtual void ResolvePromise(uint32_t aPromiseId) MOZ_OVERRIDE {}
   virtual void RejectPromise(uint32_t aPromiseId,
                              nsresult aException,
                              const nsCString& aSessionId) MOZ_OVERRIDE { }
   virtual void ExpirationChange(const nsCString& aSessionId,
                                 GMPTimestamp aExpiryTime) MOZ_OVERRIDE {}
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -106,45 +106,47 @@ ClearKeyDecryptionManager::Init(GMPDecry
   CK_LOGD("ClearKeyDecryptionManager::Init");
   mCallback = aCallback;
   mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
                              GMP_EME_CAP_DECRYPT_VIDEO);
   ClearKeyPersistence::EnsureInitialized();
 }
 
 void
-ClearKeyDecryptionManager::CreateSession(uint32_t aPromiseId,
+ClearKeyDecryptionManager::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);
 
   // 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(aPromiseId, aInitData, aInitDataSize);
+  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);
@@ -211,21 +213,16 @@ ClearKeyDecryptionManager::PersistentSes
     return;
   }
 
   ClearKeySession* session = new ClearKeySession(aSessionId,
                                                  mCallback,
                                                  kGMPPersistentSession);
   mSessions[aSessionId] = session;
 
-  // TODO: currently we have to resolve the load-session promise before we
-  // can mark the keys as usable. We should really do this before marking
-  // the keys usable, but we need to fix Gecko first.
-  mCallback->ResolveLoadSessionPromise(aPromiseId, true);
-
   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);
@@ -235,16 +232,18 @@ ClearKeyDecryptionManager::PersistentSes
 
     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)
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
@@ -19,17 +19,18 @@ class ClearKeyDecryptor;
 class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
                                           , public RefCounted
 {
 public:
   ClearKeyDecryptionManager();
 
   virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
 
-  virtual void CreateSession(uint32_t aPromiseId,
+  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,
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -93,58 +93,64 @@ ClearKeyPersistence::GetNewSessionId(GMP
 
   return sessionId;
 }
 
 
 class CreateSessionTask : public GMPTask {
 public:
   CreateSessionTask(ClearKeyDecryptionManager* aTarget,
+                    uint32_t aCreateSessionToken,
                     uint32_t aPromiseId,
                     const uint8_t* aInitData,
                     uint32_t aInitDataSize,
                     GMPSessionType aSessionType)
     : mTarget(aTarget)
+    , mCreateSessionToken(aCreateSessionToken)
     , mPromiseId(aPromiseId)
     , mSessionType(aSessionType)
   {
     mInitData.insert(mInitData.end(),
                      aInitData,
                      aInitData + aInitDataSize);
   }
   virtual void Run() MOZ_OVERRIDE {
-    mTarget->CreateSession(mPromiseId,
+    mTarget->CreateSession(mCreateSessionToken,
+                           mPromiseId,
                            "cenc",
                            strlen("cenc"),
                            &mInitData.front(),
                            mInitData.size(),
                            mSessionType);
   }
   virtual void Destroy() MOZ_OVERRIDE {
     delete this;
   }
 private:
   RefPtr<ClearKeyDecryptionManager> mTarget;
+  uint32_t mCreateSessionToken;
   uint32_t mPromiseId;
   vector<uint8_t> mInitData;
   GMPSessionType mSessionType;
 };
 
 
 /* static */ bool
 ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+                                                  uint32_t aCreateSessionToken,
                                                   uint32_t aPromiseId,
                                                   const uint8_t* aInitData,
                                                   uint32_t aInitDataSize,
                                                   GMPSessionType aSessionType)
 {
   if (sPersistentKeyState >= LOADED)  {
     return false;
   }
   GMPTask* t = new CreateSessionTask(aInstance,
+                                     aCreateSessionToken,
                                      aPromiseId,
                                      aInitData,
                                      aInitDataSize,
                                      aSessionType);
   sTasksBlockedOnSessionIdLoad.push_back(t);
   return true;
 }
 
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -12,16 +12,17 @@ class ClearKeyDecryptionManager;
 
 class ClearKeyPersistence {
 public:
   static void EnsureInitialized();
 
   static std::string GetNewSessionId(GMPSessionType aSessionType);
 
   static bool DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+                                           uint32_t aCreateSessionToken,
                                            uint32_t aPromiseId,
                                            const uint8_t* aInitData,
                                            uint32_t aInitDataSize,
                                            GMPSessionType aSessionType);
 
   static bool DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
                                          uint32_t aPromiseId,
                                          const char* aSessionId,
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -24,29 +24,32 @@ ClearKeySession::ClearKeySession(const s
 }
 
 ClearKeySession::~ClearKeySession()
 {
   CK_LOGD("ClearKeySession dtor %p", this);
 }
 
 void
-ClearKeySession::Init(uint32_t aPromiseId,
+ClearKeySession::Init(uint32_t aCreateSessionToken,
+                      uint32_t aPromiseId,
                       const uint8_t* aInitData, uint32_t aInitDataSize)
 {
   CK_LOGD("ClearKeySession::Init");
 
   ClearKeyUtils::ParseInitData(aInitData, aInitDataSize, mKeyIds);
   if (!mKeyIds.size()) {
     const char message[] = "Couldn't parse cenc key init data";
     mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message));
     return;
   }
-  mCallback->ResolveNewSessionPromise(aPromiseId,
-                                      mSessionId.data(), mSessionId.length());
+
+  mCallback->SetSessionId(aCreateSessionToken, &mSessionId[0], mSessionId.length());
+
+  mCallback->ResolvePromise(aPromiseId);
 }
 
 GMPSessionType
 ClearKeySession::Type() const
 {
   return mSessionType;
 }
 
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -19,17 +19,18 @@ public:
   explicit ClearKeySession(const std::string& aSessionId,
                            GMPDecryptorCallback* aCallback,
                            GMPSessionType aSessionType);
 
   ~ClearKeySession();
 
   const std::vector<KeyId>& GetKeyIds() const { return mKeyIds; }
 
-  void Init(uint32_t aPromiseId,
+  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; }
 
--- a/media/gmp-clearkey/0.1/clearkey.info
+++ b/media/gmp-clearkey/0.1/clearkey.info
@@ -1,4 +1,4 @@
 Name: clearkey
 Description: ClearKey decrypt-only GMP plugin
 Version: 0.1
-APIs: eme-decrypt[org.w3.clearkey]
+APIs: eme-decrypt-v2[org.w3.clearkey]