LCOV - code coverage report
Current view: top level - dom/media/eme - MediaKeySession.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 289 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 37 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/dom/HTMLMediaElement.h"
       8             : #include "mozilla/dom/MediaKeySession.h"
       9             : #include "mozilla/dom/MediaKeyError.h"
      10             : #include "mozilla/dom/MediaKeyMessageEvent.h"
      11             : #include "mozilla/dom/MediaEncryptedEvent.h"
      12             : #include "mozilla/dom/MediaKeyStatusMap.h"
      13             : #include "mozilla/dom/MediaKeySystemAccess.h"
      14             : #include "mozilla/dom/KeyIdsInitDataBinding.h"
      15             : #include "nsCycleCollectionParticipant.h"
      16             : #include "mozilla/CDMProxy.h"
      17             : #include "mozilla/AsyncEventDispatcher.h"
      18             : #include "mozilla/Move.h"
      19             : #include "mozilla/EMEUtils.h"
      20             : #include "mozilla/Encoding.h"
      21             : #include "GMPUtils.h"
      22             : #include "nsPrintfCString.h"
      23             : #include "psshparser/PsshParser.h"
      24             : #include <ctime>
      25             : 
      26             : namespace mozilla {
      27             : namespace dom {
      28             : 
      29           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaKeySession,
      30             :                                    DOMEventTargetHelper,
      31             :                                    mMediaKeyError,
      32             :                                    mKeys,
      33             :                                    mKeyStatusMap,
      34             :                                    mClosed)
      35             : 
      36           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaKeySession)
      37           0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
      38             : 
      39           0 : NS_IMPL_ADDREF_INHERITED(MediaKeySession, DOMEventTargetHelper)
      40           0 : NS_IMPL_RELEASE_INHERITED(MediaKeySession, DOMEventTargetHelper)
      41             : 
      42             : // Count of number of instances. Used to give each instance a
      43             : // unique token.
      44             : static uint32_t sMediaKeySessionNum = 0;
      45             : 
      46             : // Max length of keyId in EME "keyIds" or WebM init data format, as enforced
      47             : // by web platform tests.
      48             : static const uint32_t MAX_KEY_ID_LENGTH = 512;
      49             : 
      50             : // Max length of CENC PSSH init data tolerated, as enforced by web
      51             : // platform tests.
      52             : static const uint32_t MAX_CENC_INIT_DATA_LENGTH = 64 * 1024;
      53             : 
      54             : 
      55           0 : MediaKeySession::MediaKeySession(JSContext* aCx,
      56             :                                  nsPIDOMWindowInner* aParent,
      57             :                                  MediaKeys* aKeys,
      58             :                                  const nsAString& aKeySystem,
      59             :                                  MediaKeySessionType aSessionType,
      60           0 :                                  ErrorResult& aRv)
      61             :   : DOMEventTargetHelper(aParent)
      62             :   , mKeys(aKeys)
      63             :   , mKeySystem(aKeySystem)
      64             :   , mSessionType(aSessionType)
      65           0 :   , mToken(sMediaKeySessionNum++)
      66             :   , mIsClosed(false)
      67             :   , mUninitialized(true)
      68           0 :   , mKeyStatusMap(new MediaKeyStatusMap(aParent))
      69           0 :   , mExpiration(JS::GenericNaN())
      70             : {
      71           0 :   EME_LOG("MediaKeySession[%p,''] ctor", this);
      72             : 
      73           0 :   MOZ_ASSERT(aParent);
      74           0 :   if (aRv.Failed()) {
      75           0 :     return;
      76             :   }
      77           0 :   mClosed = MakePromise(aRv, NS_LITERAL_CSTRING("MediaKeys.createSession"));
      78             : }
      79             : 
      80           0 : void MediaKeySession::SetSessionId(const nsAString& aSessionId)
      81             : {
      82           0 :   EME_LOG("MediaKeySession[%p,'%s'] session Id set",
      83             :           this, NS_ConvertUTF16toUTF8(aSessionId).get());
      84             : 
      85           0 :   if (NS_WARN_IF(!mSessionId.IsEmpty())) {
      86           0 :     return;
      87             :   }
      88           0 :   mSessionId = aSessionId;
      89           0 :   mKeys->OnSessionIdReady(this);
      90             : }
      91             : 
      92           0 : MediaKeySession::~MediaKeySession()
      93             : {
      94           0 : }
      95             : 
      96             : MediaKeyError*
      97           0 : MediaKeySession::GetError() const
      98             : {
      99           0 :   return mMediaKeyError;
     100             : }
     101             : 
     102             : void
     103           0 : MediaKeySession::GetSessionId(nsString& aSessionId) const
     104             : {
     105           0 :   aSessionId = GetSessionId();
     106           0 : }
     107             : 
     108             : const nsString&
     109           0 : MediaKeySession::GetSessionId() const
     110             : {
     111           0 :   return mSessionId;
     112             : }
     113             : 
     114             : JSObject*
     115           0 : MediaKeySession::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     116             : {
     117           0 :   return MediaKeySessionBinding::Wrap(aCx, this, aGivenProto);
     118             : }
     119             : 
     120             : double
     121           0 : MediaKeySession::Expiration() const
     122             : {
     123           0 :   return mExpiration;
     124             : }
     125             : 
     126             : Promise*
     127           0 : MediaKeySession::Closed() const
     128             : {
     129           0 :   return mClosed;
     130             : }
     131             : 
     132             : void
     133           0 : MediaKeySession::UpdateKeyStatusMap()
     134             : {
     135           0 :   MOZ_ASSERT(!IsClosed());
     136           0 :   if (!mKeys->GetCDMProxy()) {
     137           0 :     return;
     138             :   }
     139             : 
     140           0 :   nsTArray<CDMCaps::KeyStatus> keyStatuses;
     141             :   {
     142           0 :     CDMCaps::AutoLock caps(mKeys->GetCDMProxy()->Capabilites());
     143           0 :     caps.GetKeyStatusesForSession(mSessionId, keyStatuses);
     144             :   }
     145             : 
     146           0 :   mKeyStatusMap->Update(keyStatuses);
     147             : 
     148           0 :   if (EME_LOG_ENABLED()) {
     149             :     nsAutoCString message(
     150           0 :       nsPrintfCString("MediaKeySession[%p,'%s'] key statuses change {",
     151           0 :                       this, NS_ConvertUTF16toUTF8(mSessionId).get()));
     152             :     using IntegerType = typename std::underlying_type<MediaKeyStatus>::type;
     153           0 :     for (const CDMCaps::KeyStatus& status : keyStatuses) {
     154           0 :       message.Append(nsPrintfCString(" (%s,%s)", ToHexString(status.mId).get(),
     155           0 :         MediaKeyStatusValues::strings[static_cast<IntegerType>(status.mStatus)].value));
     156             :     }
     157           0 :     message.Append(" }");
     158             :     // Use %s so we aren't exposing random strings to printf interpolation.
     159           0 :     EME_LOG("%s", message.get());
     160             :   }
     161             : }
     162             : 
     163             : MediaKeyStatusMap*
     164           0 : MediaKeySession::KeyStatuses() const
     165             : {
     166           0 :   return mKeyStatusMap;
     167             : }
     168             : 
     169             : // The user agent MUST thoroughly validate the Initialization Data before
     170             : // passing it to the CDM. This includes verifying that the length and
     171             : // values of fields are reasonable, verifying that values are within
     172             : // reasonable limits, and stripping irrelevant, unsupported, or unknown
     173             : // data or fields. It is RECOMMENDED that user agents pre-parse, sanitize,
     174             : // and/or generate a fully sanitized version of the Initialization Data.
     175             : // If the Initialization Data format specified by initDataType supports
     176             : // multiple entries, the user agent SHOULD remove entries that are not
     177             : // needed by the CDM. The user agent MUST NOT re-order entries within
     178             : // the Initialization Data.
     179             : static bool
     180           0 : ValidateInitData(const nsTArray<uint8_t>& aInitData, const nsAString& aInitDataType)
     181             : {
     182           0 :   if (aInitDataType.LowerCaseEqualsLiteral("webm")) {
     183             :     // WebM initData consists of a single keyId. Ensure it's of reasonable length.
     184           0 :     return aInitData.Length() <= MAX_KEY_ID_LENGTH;
     185           0 :   } else if (aInitDataType.LowerCaseEqualsLiteral("cenc")) {
     186             :     // Limit initData to less than 64KB.
     187           0 :     if (aInitData.Length() > MAX_CENC_INIT_DATA_LENGTH) {
     188           0 :       return false;
     189             :     }
     190           0 :     std::vector<std::vector<uint8_t>> keyIds;
     191           0 :     return ParseCENCInitData(aInitData.Elements(), aInitData.Length(), keyIds);
     192           0 :   } else if (aInitDataType.LowerCaseEqualsLiteral("keyids")) {
     193           0 :     if (aInitData.Length() > MAX_KEY_ID_LENGTH) {
     194           0 :       return false;
     195             :     }
     196             :     // Ensure that init data matches the expected JSON format.
     197           0 :     mozilla::dom::KeyIdsInitData keyIds;
     198           0 :     nsString json;
     199           0 :     nsDependentCSubstring raw(reinterpret_cast<const char*>(aInitData.Elements()), aInitData.Length());
     200           0 :     if (NS_FAILED(UTF_8_ENCODING->DecodeWithBOMRemoval(raw, json))) {
     201           0 :       return false;
     202             :     }
     203           0 :     if (!keyIds.Init(json)) {
     204           0 :       return false;
     205             :     }
     206           0 :     if (keyIds.mKids.Length() == 0) {
     207           0 :       return false;
     208             :     }
     209           0 :     for (const auto& kid : keyIds.mKids) {
     210           0 :       if (kid.IsEmpty()) {
     211           0 :         return false;
     212             :       }
     213             :     }
     214             :   }
     215           0 :   return true;
     216             : }
     217             : 
     218             : // Generates a license request based on the initData. A message of type
     219             : // "license-request" or "individualization-request" will always be queued
     220             : // if the algorithm succeeds and the promise is resolved.
     221             : already_AddRefed<Promise>
     222           0 : MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
     223             :                                  const ArrayBufferViewOrArrayBuffer& aInitData,
     224             :                                  ErrorResult& aRv)
     225             : {
     226           0 :   RefPtr<DetailedPromise> promise(MakePromise(aRv,
     227           0 :     NS_LITERAL_CSTRING("MediaKeySession.generateRequest")));
     228           0 :   if (aRv.Failed()) {
     229           0 :     return nullptr;
     230             :   }
     231             : 
     232             :   // If this object is closed, return a promise rejected with an InvalidStateError.
     233           0 :   if (IsClosed()) {
     234           0 :     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, closed",
     235             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     236           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     237           0 :       NS_LITERAL_CSTRING("Session is closed in MediaKeySession.generateRequest()"));
     238           0 :     return promise.forget();
     239             :   }
     240             : 
     241             :   // If this object's uninitialized value is false, return a promise rejected
     242             :   // with an InvalidStateError.
     243           0 :   if (!mUninitialized) {
     244           0 :     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, uninitialized",
     245             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     246           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     247           0 :       NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.generateRequest()"));
     248           0 :     return promise.forget();
     249             :   }
     250             : 
     251             :   // Let this object's uninitialized value be false.
     252           0 :   mUninitialized = false;
     253             : 
     254             :   // If initDataType is the empty string, return a promise rejected
     255             :   // with a newly created TypeError.
     256           0 :   if (aInitDataType.IsEmpty()) {
     257           0 :     promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
     258           0 :       NS_LITERAL_CSTRING("Empty initDataType passed to MediaKeySession.generateRequest()"));
     259           0 :     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initDataType",
     260             :       this, NS_ConvertUTF16toUTF8(mSessionId).get());
     261           0 :     return promise.forget();
     262             :   }
     263             : 
     264             :   // If initData is an empty array, return a promise rejected with
     265             :   // a newly created TypeError.
     266           0 :   nsTArray<uint8_t> data;
     267           0 :   CopyArrayBufferViewOrArrayBufferData(aInitData, data);
     268           0 :   if (data.IsEmpty()) {
     269           0 :     promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
     270           0 :       NS_LITERAL_CSTRING("Empty initData passed to MediaKeySession.generateRequest()"));
     271           0 :     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initData",
     272             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     273           0 :     return promise.forget();
     274             :   }
     275             : 
     276             :   // If the Key System implementation represented by this object's
     277             :   // cdm implementation value does not support initDataType as an
     278             :   // Initialization Data Type, return a promise rejected with a
     279             :   // NotSupportedError. String comparison is case-sensitive.
     280           0 :   if (!MediaKeySystemAccess::KeySystemSupportsInitDataType(mKeySystem, aInitDataType)) {
     281           0 :     promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
     282           0 :       NS_LITERAL_CSTRING("Unsupported initDataType passed to MediaKeySession.generateRequest()"));
     283           0 :     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, unsupported initDataType",
     284             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     285           0 :     return promise.forget();
     286             :   }
     287             : 
     288             :   // Let init data be a copy of the contents of the initData parameter.
     289             :   // Note: Handled by the CopyArrayBufferViewOrArrayBufferData call above.
     290             : 
     291             :   // Let session type be this object's session type.
     292             : 
     293             :   // Let promise be a new promise.
     294             : 
     295             :   // Run the following steps in parallel:
     296             : 
     297             :   // If the init data is not valid for initDataType, reject promise with
     298             :   // a newly created TypeError.
     299           0 :   if (!ValidateInitData(data, aInitDataType)) {
     300             :     // If the preceding step failed, reject promise with a newly created TypeError.
     301           0 :     promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
     302           0 :       NS_LITERAL_CSTRING("initData sanitization failed in MediaKeySession.generateRequest()"));
     303           0 :     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() initData sanitization failed",
     304             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     305           0 :     return promise.forget();
     306             :   }
     307             : 
     308             :   // Let sanitized init data be a validated and sanitized version of init data.
     309             : 
     310             :   // If sanitized init data is empty, reject promise with a NotSupportedError.
     311             : 
     312             :   // Note: Remaining steps of generateRequest method continue in CDM.
     313             : 
     314             :   // Convert initData to hex for easier logging.
     315             :   // Note: CreateSession() Move()s the data out of the array, so we have
     316             :   // to copy it here.
     317           0 :   nsAutoCString hexInitData(ToHexString(data));
     318           0 :   PromiseId pid = mKeys->StorePromise(promise);
     319           0 :   mKeys->ConnectPendingPromiseIdWithToken(pid, Token());
     320           0 :   mKeys->GetCDMProxy()->CreateSession(Token(),
     321           0 :                                       mSessionType,
     322             :                                       pid,
     323           0 :                                       aInitDataType, data);
     324             : 
     325           0 :   EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() sent, "
     326             :           "promiseId=%d initData='%s' initDataType='%s'",
     327             :           this,
     328             :           NS_ConvertUTF16toUTF8(mSessionId).get(),
     329             :           pid,
     330             :           hexInitData.get(),
     331             :           NS_ConvertUTF16toUTF8(aInitDataType).get());
     332             : 
     333           0 :   return promise.forget();
     334             : }
     335             : 
     336             : already_AddRefed<Promise>
     337           0 : MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
     338             : {
     339           0 :   RefPtr<DetailedPromise> promise(MakePromise(aRv,
     340           0 :     NS_LITERAL_CSTRING("MediaKeySession.load")));
     341           0 :   if (aRv.Failed()) {
     342           0 :     return nullptr;
     343             :   }
     344             : 
     345             :   // 1. If this object is closed, return a promise rejected with an InvalidStateError.
     346           0 :   if (IsClosed()) {
     347           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     348           0 :                          NS_LITERAL_CSTRING("Session is closed in MediaKeySession.load()"));
     349           0 :     EME_LOG("MediaKeySession[%p,'%s'] Load() failed, closed",
     350             :       this, NS_ConvertUTF16toUTF8(aSessionId).get());
     351           0 :     return promise.forget();
     352             :   }
     353             : 
     354             :   // 2.If this object's uninitialized value is false, return a promise rejected
     355             :   // with an InvalidStateError.
     356           0 :   if (!mUninitialized) {
     357           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     358           0 :                          NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.load()"));
     359           0 :     EME_LOG("MediaKeySession[%p,'%s'] Load() failed, uninitialized",
     360             :       this, NS_ConvertUTF16toUTF8(aSessionId).get());
     361           0 :     return promise.forget();
     362             :   }
     363             : 
     364             :   // 3.Let this object's uninitialized value be false.
     365           0 :   mUninitialized = false;
     366             : 
     367             :   // 4. If sessionId is the empty string, return a promise rejected with a newly created TypeError.
     368           0 :   if (aSessionId.IsEmpty()) {
     369           0 :     promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
     370           0 :                          NS_LITERAL_CSTRING("Trying to load a session with empty session ID"));
     371             :     // "The sessionId parameter is empty."
     372           0 :     EME_LOG("MediaKeySession[%p,''] Load() failed, no sessionId", this);
     373           0 :     return promise.forget();
     374             :   }
     375             : 
     376             :   // 5. If the result of running the Is persistent session type? algorithm
     377             :   // on this object's session type is false, return a promise rejected with
     378             :   // a newly created TypeError.
     379           0 :   if (mSessionType == MediaKeySessionType::Temporary) {
     380           0 :     promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
     381           0 :                          NS_LITERAL_CSTRING("Trying to load() into a non-persistent session"));
     382           0 :     EME_LOG("MediaKeySession[%p,''] Load() failed, can't load in a non-persistent session", this);
     383           0 :     return promise.forget();
     384             :   }
     385             : 
     386             :   // Note: We don't support persistent sessions in any keysystem, so all calls
     387             :   // to Load() should reject with a TypeError in the preceding check. Omitting
     388             :   // implementing the rest of the specified MediaKeySession::Load() algorithm.
     389             : 
     390             :   // We now know the sessionId being loaded into this session. Remove the
     391             :   // session from its owning MediaKey's set of sessions awaiting a sessionId.
     392           0 :   RefPtr<MediaKeySession> session(mKeys->GetPendingSession(Token()));
     393           0 :   MOZ_ASSERT(session == this, "Session should be awaiting id on its own token");
     394             : 
     395             :   // Associate with the known sessionId.
     396           0 :   SetSessionId(aSessionId);
     397             : 
     398           0 :   PromiseId pid = mKeys->StorePromise(promise);
     399           0 :   mKeys->GetCDMProxy()->LoadSession(pid, mSessionType, aSessionId);
     400             : 
     401           0 :   EME_LOG("MediaKeySession[%p,'%s'] Load() sent to CDM, promiseId=%d",
     402             :     this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
     403             : 
     404           0 :   return promise.forget();
     405             : }
     406             : 
     407             : already_AddRefed<Promise>
     408           0 : MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResult& aRv)
     409             : {
     410           0 :   RefPtr<DetailedPromise> promise(MakePromise(aRv,
     411           0 :     NS_LITERAL_CSTRING("MediaKeySession.update")));
     412           0 :   if (aRv.Failed()) {
     413           0 :     return nullptr;
     414             :   }
     415             : 
     416           0 :   if (!IsCallable()) {
     417             :     // If this object's callable value is false, return a promise rejected
     418             :     // with a new DOMException whose name is InvalidStateError.
     419           0 :     EME_LOG("MediaKeySession[%p,''] Update() called before sessionId set by CDM", this);
     420           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     421           0 :       NS_LITERAL_CSTRING("MediaKeySession.Update() called before sessionId set by CDM"));
     422           0 :     return promise.forget();
     423             :   }
     424             : 
     425           0 :   nsTArray<uint8_t> data;
     426           0 :   if (IsClosed() || !mKeys->GetCDMProxy()) {
     427           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     428           0 :                          NS_LITERAL_CSTRING("Session is closed or was not properly initialized"));
     429           0 :     EME_LOG("MediaKeySession[%p,'%s'] Update() failed, session is closed or was not properly initialised.",
     430             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     431           0 :     return promise.forget();
     432             :   }
     433           0 :   CopyArrayBufferViewOrArrayBufferData(aResponse, data);
     434           0 :   if (data.IsEmpty()) {
     435           0 :     promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
     436           0 :       NS_LITERAL_CSTRING("Empty response buffer passed to MediaKeySession.update()"));
     437           0 :     EME_LOG("MediaKeySession[%p,'%s'] Update() failed, empty response buffer",
     438             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     439           0 :     return promise.forget();
     440             :   }
     441             : 
     442             : 
     443             :   // Convert response to hex for easier logging.
     444             :   // Note: UpdateSession() Move()s the data out of the array, so we have
     445             :   // to copy it here.
     446           0 :   nsAutoCString hexResponse(ToHexString(data));
     447             : 
     448           0 :   PromiseId pid = mKeys->StorePromise(promise);
     449           0 :   mKeys->GetCDMProxy()->UpdateSession(mSessionId,
     450             :                                       pid,
     451           0 :                                       data);
     452             : 
     453           0 :   EME_LOG("MediaKeySession[%p,'%s'] Update() sent to CDM, "
     454             :           "promiseId=%d Response='%s'",
     455             :            this,
     456             :            NS_ConvertUTF16toUTF8(mSessionId).get(),
     457             :            pid,
     458             :            hexResponse.get());
     459             : 
     460           0 :   return promise.forget();
     461             : }
     462             : 
     463             : already_AddRefed<Promise>
     464           0 : MediaKeySession::Close(ErrorResult& aRv)
     465             : {
     466           0 :   RefPtr<DetailedPromise> promise(MakePromise(aRv,
     467           0 :     NS_LITERAL_CSTRING("MediaKeySession.close")));
     468           0 :   if (aRv.Failed()) {
     469           0 :     return nullptr;
     470             :   }
     471             :   // 1. Let session be the associated MediaKeySession object.
     472             :   // 2. If session is closed, return a resolved promise.
     473           0 :   if (IsClosed()) {
     474           0 :     EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
     475             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     476           0 :     promise->MaybeResolveWithUndefined();
     477           0 :     return promise.forget();
     478             :   }
     479             :   // 3. If session's callable value is false, return a promise rejected
     480             :   // with an InvalidStateError.
     481           0 :   if (!IsCallable()) {
     482           0 :     EME_LOG("MediaKeySession[%p,''] Close() called before sessionId set by CDM", this);
     483           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     484           0 :       NS_LITERAL_CSTRING("MediaKeySession.Close() called before sessionId set by CDM"));
     485           0 :     return promise.forget();
     486             :   }
     487           0 :   if (!mKeys->GetCDMProxy()) {
     488           0 :     EME_LOG("MediaKeySession[%p,'%s'] Close() null CDMProxy",
     489             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     490           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     491           0 :       NS_LITERAL_CSTRING("MediaKeySession.Close() lost reference to CDM"));
     492           0 :     return promise.forget();
     493             :   }
     494             :   // 4. Let promise be a new promise.
     495           0 :   PromiseId pid = mKeys->StorePromise(promise);
     496             :   // 5. Run the following steps in parallel:
     497             :   // 5.1 Let cdm be the CDM instance represented by session's cdm instance value.
     498             :   // 5.2 Use cdm to close the session associated with session.
     499           0 :   mKeys->GetCDMProxy()->CloseSession(mSessionId, pid);
     500             : 
     501           0 :   EME_LOG("MediaKeySession[%p,'%s'] Close() sent to CDM, promiseId=%d",
     502             :           this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
     503             : 
     504             :   // Session Closed algorithm is run when CDM causes us to run OnSessionClosed().
     505             : 
     506             :   // 6. Return promise.
     507           0 :   return promise.forget();
     508             : }
     509             : 
     510             : void
     511           0 : MediaKeySession::OnClosed()
     512             : {
     513           0 :   if (IsClosed()) {
     514           0 :     return;
     515             :   }
     516           0 :   EME_LOG("MediaKeySession[%p,'%s'] session close operation complete.",
     517             :           this, NS_ConvertUTF16toUTF8(mSessionId).get());
     518           0 :   mIsClosed = true;
     519           0 :   mKeys->OnSessionClosed(this);
     520           0 :   mKeys = nullptr;
     521           0 :   mClosed->MaybeResolveWithUndefined();
     522             : }
     523             : 
     524             : bool
     525           0 : MediaKeySession::IsClosed() const
     526             : {
     527           0 :   return mIsClosed;
     528             : }
     529             : 
     530             : already_AddRefed<Promise>
     531           0 : MediaKeySession::Remove(ErrorResult& aRv)
     532             : {
     533           0 :   RefPtr<DetailedPromise> promise(MakePromise(aRv,
     534           0 :     NS_LITERAL_CSTRING("MediaKeySession.remove")));
     535           0 :   if (aRv.Failed()) {
     536           0 :     return nullptr;
     537             :   }
     538           0 :   if (!IsCallable()) {
     539             :     // If this object's callable value is false, return a promise rejected
     540             :     // with a new DOMException whose name is InvalidStateError.
     541           0 :     EME_LOG("MediaKeySession[%p,''] Remove() called before sessionId set by CDM", this);
     542           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     543           0 :       NS_LITERAL_CSTRING("MediaKeySession.Remove() called before sessionId set by CDM"));
     544           0 :     return promise.forget();
     545             :   }
     546           0 :   if (mSessionType != MediaKeySessionType::Persistent_license) {
     547           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
     548           0 :                          NS_LITERAL_CSTRING("Calling MediaKeySession.remove() on non-persistent session"));
     549             :     // "The operation is not supported on session type sessions."
     550           0 :     EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, sesion not persisrtent.",
     551             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     552           0 :     return promise.forget();
     553             :   }
     554           0 :   if (IsClosed() || !mKeys->GetCDMProxy()) {
     555           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
     556           0 :                          NS_LITERAL_CSTRING("MediaKeySesison.remove() called but session is not active"));
     557             :     // "The session is closed."
     558           0 :     EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, already session closed.",
     559             :             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     560           0 :     return promise.forget();
     561             :   }
     562           0 :   PromiseId pid = mKeys->StorePromise(promise);
     563           0 :   mKeys->GetCDMProxy()->RemoveSession(mSessionId, pid);
     564           0 :   EME_LOG("MediaKeySession[%p,'%s'] Remove() sent to CDM, promiseId=%d.",
     565             :           this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
     566             : 
     567           0 :   return promise.forget();
     568             : }
     569             : 
     570             : void
     571           0 : MediaKeySession::DispatchKeyMessage(MediaKeyMessageType aMessageType,
     572             :                                     const nsTArray<uint8_t>& aMessage)
     573             : {
     574           0 :   if (EME_LOG_ENABLED()) {
     575           0 :     EME_LOG("MediaKeySession[%p,'%s'] DispatchKeyMessage() type=%s message='%s'",
     576             :             this, NS_ConvertUTF16toUTF8(mSessionId).get(),
     577             :             MediaKeyMessageTypeValues::strings[uint32_t(aMessageType)].value,
     578             :             ToHexString(aMessage).get());
     579             :   }
     580             : 
     581             :   RefPtr<MediaKeyMessageEvent> event(
     582           0 :     MediaKeyMessageEvent::Constructor(this, aMessageType, aMessage));
     583             :   RefPtr<AsyncEventDispatcher> asyncDispatcher =
     584           0 :     new AsyncEventDispatcher(this, event);
     585           0 :   asyncDispatcher->PostDOMEvent();
     586           0 : }
     587             : 
     588             : void
     589           0 : MediaKeySession::DispatchKeyError(uint32_t aSystemCode)
     590             : {
     591           0 :   EME_LOG("MediaKeySession[%p,'%s'] DispatchKeyError() systemCode=%u.",
     592             :           this, NS_ConvertUTF16toUTF8(mSessionId).get(), aSystemCode);
     593             : 
     594           0 :   RefPtr<MediaKeyError> event(new MediaKeyError(this, aSystemCode));
     595             :   RefPtr<AsyncEventDispatcher> asyncDispatcher =
     596           0 :     new AsyncEventDispatcher(this, event);
     597           0 :   asyncDispatcher->PostDOMEvent();
     598           0 : }
     599             : 
     600             : void
     601           0 : MediaKeySession::DispatchKeyStatusesChange()
     602             : {
     603           0 :   if (IsClosed()) {
     604           0 :     return;
     605             :   }
     606             : 
     607           0 :   UpdateKeyStatusMap();
     608             : 
     609             :   RefPtr<AsyncEventDispatcher> asyncDispatcher =
     610           0 :     new AsyncEventDispatcher(this, NS_LITERAL_STRING("keystatuseschange"), false);
     611           0 :   asyncDispatcher->PostDOMEvent();
     612             : }
     613             : 
     614             : uint32_t
     615           0 : MediaKeySession::Token() const
     616             : {
     617           0 :   return mToken;
     618             : }
     619             : 
     620             : already_AddRefed<DetailedPromise>
     621           0 : MediaKeySession::MakePromise(ErrorResult& aRv, const nsACString& aName)
     622             : {
     623           0 :   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
     624           0 :   if (!global) {
     625           0 :     NS_WARNING("Passed non-global to MediaKeys ctor!");
     626           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
     627           0 :     return nullptr;
     628             :   }
     629           0 :   return DetailedPromise::Create(global, aRv, aName);
     630             : }
     631             : 
     632             : void
     633           0 : MediaKeySession::SetExpiration(double aExpiration)
     634             : {
     635           0 :   EME_LOG("MediaKeySession[%p,'%s'] SetExpiry(%.12lf) (%.2lf hours from now)",
     636             :           this,
     637             :           NS_ConvertUTF16toUTF8(mSessionId).get(),
     638             :           aExpiration,
     639             :           (aExpiration - 1000.0 * double(time(0))) / (1000.0 * 60 * 60));
     640           0 :   mExpiration = aExpiration;
     641           0 : }
     642             : 
     643             : EventHandlerNonNull*
     644           0 : MediaKeySession::GetOnkeystatuseschange()
     645             : {
     646           0 :   return GetEventHandler(nsGkAtoms::onkeystatuseschange, EmptyString());
     647             : }
     648             : 
     649             : void
     650           0 : MediaKeySession::SetOnkeystatuseschange(EventHandlerNonNull* aCallback)
     651             : {
     652           0 :   SetEventHandler(nsGkAtoms::onkeystatuseschange, EmptyString(), aCallback);
     653           0 : }
     654             : 
     655             : EventHandlerNonNull*
     656           0 : MediaKeySession::GetOnmessage()
     657             : {
     658           0 :   return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
     659             : }
     660             : 
     661             : void
     662           0 : MediaKeySession::SetOnmessage(EventHandlerNonNull* aCallback)
     663             : {
     664           0 :   SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
     665           0 : }
     666             : 
     667             : nsCString
     668           0 : ToCString(MediaKeySessionType aType)
     669             : {
     670             :   using IntegerType = typename std::underlying_type<MediaKeySessionType>::type;
     671           0 :   auto idx = static_cast<IntegerType>(aType);
     672           0 :   return nsDependentCString(MediaKeySessionTypeValues::strings[idx].value);
     673             : }
     674             : 
     675             : nsString
     676           0 : ToString(MediaKeySessionType aType)
     677             : {
     678           0 :   return NS_ConvertUTF8toUTF16(ToCString(aType));
     679             : }
     680             : 
     681             : } // namespace dom
     682             : } // namespace mozilla

Generated by: LCOV version 1.13