Bug 1310936- Provide a map to get pending MediaKeySession by promise Id correctly. draft
authorKilik Kuo <kikuo@mozilla.com>
Thu, 20 Oct 2016 15:28:09 +0800
changeset 427337 8bbda1ee417726da052a7b347c38f6036912089b
parent 426771 90d8afaddf9150853b0b68b35b30c1e54a8683e7
child 534438 f5aab78e256f2670269cf53778038918ffbb4b8d
push id32986
push userkikuo@mozilla.com
push dateThu, 20 Oct 2016 07:29:07 +0000
bugs1310936
milestone52.0a1
Bug 1310936- Provide a map to get pending MediaKeySession by promise Id correctly. MozReview-Commit-ID: 34BL4GYoC1A
dom/media/eme/MediaKeySession.cpp
dom/media/eme/MediaKeys.cpp
dom/media/eme/MediaKeys.h
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -64,17 +64,17 @@ MediaKeySession::MediaKeySession(JSConte
   , mCDMVersion(aCDMVersion)
   , mSessionType(aSessionType)
   , mToken(sMediaKeySessionNum++)
   , mIsClosed(false)
   , mUninitialized(true)
   , mKeyStatusMap(new MediaKeyStatusMap(aParent))
   , mExpiration(JS::GenericNaN())
 {
-  EME_LOG("MediaKeySession[%p,''] session Id set", this);
+  EME_LOG("MediaKeySession[%p,''] ctor", this);
 
   MOZ_ASSERT(aParent);
   if (aRv.Failed()) {
     return;
   }
   mClosed = MakePromise(aRv, NS_LITERAL_CSTRING("MediaKeys.createSession"));
 }
 
@@ -320,16 +320,17 @@ MediaKeySession::GenerateRequest(const n
   Telemetry::Accumulate(Telemetry::VIDEO_CDM_GENERATE_REQUEST_CALLED,
                         ToCDMTypeTelemetryEnum(mKeySystem));
 
   // Convert initData to base64 for easier logging.
   // Note: CreateSession() Move()s the data out of the array, so we have
   // to copy it here.
   nsAutoCString base64InitData(ToBase64(data));
   PromiseId pid = mKeys->StorePromise(promise);
+  mKeys->ConnectPendingPromiseIdWithToken(pid, Token());
   mKeys->GetCDMProxy()->CreateSession(Token(),
                                       mSessionType,
                                       pid,
                                       aInitDataType, data);
 
   EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() sent, "
           "promiseId=%d initData(base64)='%s' initDataType='%s'",
           this,
@@ -385,16 +386,17 @@ MediaKeySession::Load(const nsAString& a
   // session from its owning MediaKey's set of sessions awaiting a sessionId.
   RefPtr<MediaKeySession> session(mKeys->GetPendingSession(Token()));
   MOZ_ASSERT(session == this, "Session should be awaiting id on its own token");
 
   // Associate with the known sessionId.
   SetSessionId(aSessionId);
 
   PromiseId pid = mKeys->StorePromise(promise);
+  mKeys->ConnectPendingPromiseIdWithToken(pid, Token());
   mKeys->GetCDMProxy()->LoadSession(pid, aSessionId);
 
   EME_LOG("MediaKeySession[%p,'%s'] Load() sent to CDM, promiseId=%d",
     this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
 
   return promise.forget();
 }
 
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -191,16 +191,26 @@ MediaKeys::StorePromise(DetailedPromise*
     MOZ_ASSERT(iter.Data() != aPromise);
   }
 #endif
 
   mPromises.Put(id, aPromise);
   return id;
 }
 
+void
+MediaKeys::ConnectPendingPromiseIdWithToken(PromiseId aId, uint32_t aToken)
+{
+  // Should only be called from MediaKeySession::GenerateRequest and
+  // MediaKeySession::Load.
+  mPromiseIdToken.Put(aId, aToken);
+  EME_LOG("MediaKeys[%p]::ConnectPendingPromiseIdWithToken() id=%u => token(%u)",
+          this, aId, aToken);
+}
+
 already_AddRefed<DetailedPromise>
 MediaKeys::RetrievePromise(PromiseId aId)
 {
   if (!mPromises.Contains(aId)) {
     NS_WARNING(nsPrintfCString("Tried to retrieve a non-existent promise id=%d", aId).get());
     return nullptr;
   }
   RefPtr<DetailedPromise> promise;
@@ -214,22 +224,26 @@ MediaKeys::RejectPromise(PromiseId aId, 
                          const nsCString& aReason)
 {
   EME_LOG("MediaKeys[%p]::RejectPromise(%d, 0x%x)", this, aId, aExceptionCode);
 
   RefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
-  if (mPendingSessions.Contains(aId)) {
-    // This promise could be a createSession or loadSession promise,
-    // so we might have a pending session waiting to be resolved into
-    // the promise on success. We've been directed to reject to promise,
-    // so we can throw away the corresponding session object.
-    mPendingSessions.Remove(aId);
+
+  // This promise could be a createSession or loadSession promise,
+  // so we might have a pending session waiting to be resolved into
+  // the promise on success. We've been directed to reject to promise,
+  // so we can throw away the corresponding session object.
+  uint32_t token = 0;
+  if (mPromiseIdToken.Get(aId, &token)) {
+    MOZ_ASSERT(mPendingSessions.Contains(token));
+    mPendingSessions.Remove(token);
+    mPromiseIdToken.Remove(aId);
   }
 
   MOZ_ASSERT(NS_FAILED(aExceptionCode));
   promise->MaybeReject(aExceptionCode, aReason);
 
   if (mCreatePromiseId == aId) {
     // Note: This will probably destroy the MediaKeys object!
     Release();
@@ -259,39 +273,46 @@ MediaKeys::OnSessionIdReady(MediaKeySess
 }
 
 void
 MediaKeys::ResolvePromise(PromiseId aId)
 {
   EME_LOG("MediaKeys[%p]::ResolvePromise(%d)", this, aId);
 
   RefPtr<DetailedPromise> promise(RetrievePromise(aId));
+  MOZ_ASSERT(!mPromises.Contains(aId));
   if (!promise) {
     return;
   }
-  if (mPendingSessions.Contains(aId)) {
-    // We should only resolve LoadSession calls via this path,
-    // not CreateSession() promises.
-    RefPtr<MediaKeySession> session;
-    if (!mPendingSessions.Get(aId, getter_AddRefs(session)) ||
-        !session ||
-        session->GetSessionId().IsEmpty()) {
-      NS_WARNING("Received activation for non-existent session!");
-      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
-                           NS_LITERAL_CSTRING("CDM LoadSession() returned a different session ID than requested"));
-      mPendingSessions.Remove(aId);
-      return;
-    }
-    mPendingSessions.Remove(aId);
-    mKeySessions.Put(session->GetSessionId(), session);
-    promise->MaybeResolve(session);
-  } else {
+
+  uint32_t token = 0;
+  if (!mPromiseIdToken.Get(aId, &token)) {
+    promise->MaybeResolveWithUndefined();
+    return;
+  } else if (!mPendingSessions.Contains(token)) {
+    // Pending session for CreateSession() should be removed when sessionId
+    // is ready.
     promise->MaybeResolveWithUndefined();
+    mPromiseIdToken.Remove(aId);
+    return;
   }
-  MOZ_ASSERT(!mPromises.Contains(aId));
+  mPromiseIdToken.Remove(aId);
+
+  // We should only resolve LoadSession calls via this path,
+  // not CreateSession() promises.
+  RefPtr<MediaKeySession> session;
+  mPendingSessions.Remove(token, getter_AddRefs(session));
+  if (!session || session->GetSessionId().IsEmpty()) {
+    NS_WARNING("Received activation for non-existent session!");
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("CDM LoadSession() returned a different session ID than requested"));
+    return;
+  }
+  mKeySessions.Put(session->GetSessionId(), session);
+  promise->MaybeResolve(session);
 }
 
 class MediaKeysGMPCrashHelper : public GMPCrashHelper
 {
 public:
   explicit MediaKeysGMPCrashHelper(MediaKeys* aMediaKeys)
     : mMediaKeys(aMediaKeys)
   {
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -28,16 +28,17 @@ namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
 class MediaKeySession;
 class HTMLMediaElement;
 
 typedef nsRefPtrHashtable<nsStringHashKey, MediaKeySession> KeySessionHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, dom::DetailedPromise> PromiseHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, MediaKeySession> PendingKeySessionsHashMap;
+typedef nsDataHashtable<nsUint32HashKey, uint32_t> PendingPromiseIdTokenHashMap;
 typedef uint32_t PromiseId;
 
 // This class is used on the main thread only.
 // Note: its addref/release is not (and can't be) thread safe!
 class MediaKeys final : public nsISupports,
                         public nsWrapperCache,
                         public SupportsWeakPtr<MediaKeys>
 {
@@ -102,16 +103,21 @@ public:
   // Makes a new promise, or nullptr on failure.
   already_AddRefed<DetailedPromise> MakePromise(ErrorResult& aRv,
                                                 const nsACString& aName);
   // Stores promise in mPromises, returning an ID that can be used to retrieve
   // it later. The ID is passed to the CDM, so that it can signal specific
   // promises to be resolved.
   PromiseId StorePromise(DetailedPromise* aPromise);
 
+  // Stores a map for promise id and session token, and it will be used to
+  // remove the pending sessions by promise id while creating/loading various
+  // sessions in the same time.
+  void ConnectPendingPromiseIdWithToken(PromiseId aId, uint32_t aToken);
+
   // Reject promise with DOMException corresponding to aExceptionCode.
   void RejectPromise(PromiseId aId, nsresult aExceptionCode,
                      const nsCString& aReason);
   // Resolves promise with "undefined".
   void ResolvePromise(PromiseId aId);
 
   const nsCString& GetNodeId() const;
 
@@ -146,14 +152,16 @@ private:
   PendingKeySessionsHashMap mPendingSessions;
   PromiseId mCreatePromiseId;
 
   RefPtr<nsIPrincipal> mPrincipal;
   RefPtr<nsIPrincipal> mTopLevelPrincipal;
 
   const bool mDistinctiveIdentifierRequired;
   const bool mPersistentStateRequired;
+
+  PendingPromiseIdTokenHashMap mPromiseIdToken;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mediakeys_h__