func();
} if (self->mHost) { // The session manager should be the last thing the ClearKey CDM is // waiting on to be initialized.
self->mHost->OnInitialized(true);
}
};
// If 'DecryptingComplete' has been called mHost will be null so we can't // won't be able to resolve our promise if (!mHost) {
CK_LOGD("ClearKeySessionManager::CreateSession: mHost is nullptr"); return;
}
// initDataType must be "cenc", "keyids", or "webm". if (aInitDataType != InitDataType::kCenc &&
aInitDataType != InitDataType::kKeyIds &&
aInitDataType != InitDataType::kWebM) {
string message = "initDataType is not supported by ClearKey";
mHost->OnRejectPromise(aPromiseId, Exception::kExceptionNotSupportedError, 0, message.c_str(), message.size());
for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) { // Need to request this key ID from the client. We always send a key // request, whether or not another session has sent a request with the same // key ID. Otherwise a script can end up waiting for another script to // respond to the request (which may not necessarily happen).
CK_LOGARRAY("Key ID: ", it->data(), it->size());
neededKeys.push_back(*it);
mDecryptionManager->ExpectKeyId(*it);
}
if (neededKeys.empty()) {
CK_LOGD("No keys needed from client."); return;
}
// Send a request for needed key data.
string request;
ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
// Resolve the promise with the new session information.
mHost->OnResolveNewSessionPromise(aPromiseId, sessionId.c_str(),
sessionId.size());
// Copy the sessionId into a string so the lambda captures it properly.
string sessionId(aSessionId, aSessionId + aSessionIdLength);
// Hold a reference to the SessionManager so that it isn't released before // we try to use it.
RefPtr<ClearKeySessionManager> self(this);
function<void()> deferrer = [self, aPromiseId, sessionId]() {
self->LoadSession(aPromiseId, sessionId.data(), sessionId.size());
};
if (MaybeDeferTillInitialized(std::move(deferrer))) {
CK_LOGD("Deferring LoadSession"); return;
}
// If the SessionManager has been shutdown mHost will be null and we won't // be able to resolve the promise. if (!mHost) { return;
}
if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); return;
}
if (!mPersistence->IsPersistentSessionId(sessionId)) {
mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); return;
}
function<void()> failure = [self, aPromiseId] { if (!self->mHost) { return;
} // As per the API described in ContentDecryptionModule_8
self->mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
};
// Check that the SessionManager has not been shut down before we try and // resolve any promises. if (!mHost) { return;
}
if (Contains(mSessions, aSessionId) ||
(aKeyDataSize % (2 * CENC_KEY_LEN)) != 0) { // As per the instructions in ContentDecryptionModule_8
mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0); return;
}
ClearKeySession* session = new ClearKeySession(aSessionId, SessionType::kPersistentLicense);
// Copy the method arguments so we can capture them in the lambda
string sessionId(aSessionId, aSessionId + aSessionIdLength);
vector<uint8_t> response(aResponse, aResponse + aResponseSize);
// Hold a reference to the SessionManager so it isn't released before we // callback.
RefPtr<ClearKeySessionManager> self(this); // sessionId is captured by copy (not std::move) because the outer function // still uses it below to look up the session in mSessions.
function<void()> deferrer = [self, aPromiseId, sessionId, response]() {
self->UpdateSession(aPromiseId, sessionId.data(), sessionId.size(),
response.data(), response.size());
};
// If we haven't fully loaded, defer calling this method if (MaybeDeferTillInitialized(std::move(deferrer))) {
CK_LOGD("Deferring LoadSession"); return;
}
// Make sure the SessionManager has not been shutdown before we try and // resolve any promises. if (!mHost) { return;
}
auto itr = mSessions.find(sessionId); if (itr == mSessions.end() || !(itr->second)) {
CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
CK_LOGD("Unable to find session: %s", sessionId.c_str());
mHost->OnRejectPromise(aPromiseId, Exception::kExceptionTypeError, 0,
nullptr, 0);
return;
}
ClearKeySession* session = itr->second;
// Verify the size of session response. if (aResponseSize >= kMaxSessionResponseLength) {
CK_LOGW("Session response size is not within a reasonable size.");
CK_LOGD("Failed to parse response for session %s", sessionId.c_str());
// Parse the response for any (key ID, key) pairs.
vector<KeyIdPair> keyPairs; if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs,
session->Type())) {
CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
if (session->Type() != SessionType::kPersistentLicense) {
mHost->OnResolvePromise(aPromiseId); return;
}
// Store the keys on disk. We store a record whose name is the sessionId, // and simply append each keyId followed by its key.
vector<uint8_t> keydata;
Serialize(session, keydata);
// Copy the sessionId into a string so we capture it properly.
string sessionId(aSessionId, aSessionId + aSessionIdLength); // Hold a reference to the session manager, so it doesn't get deleted // before we need to use it.
RefPtr<ClearKeySessionManager> self(this);
function<void()> deferrer = [self, aPromiseId, sessionId]() {
self->CloseSession(aPromiseId, sessionId.data(), sessionId.size());
};
// If we haven't loaded, call this method later. if (MaybeDeferTillInitialized(std::move(deferrer))) {
CK_LOGD("Deferring CloseSession"); return;
}
// If DecryptingComplete has been called mHost will be null and we won't // be able to resolve our promise. if (!mHost) { return;
}
auto itr = mSessions.find(sessionId); if (itr == mSessions.end()) {
CK_LOGW("ClearKey CDM couldn't close non-existent session.");
mHost->OnRejectPromise(aPromiseId, Exception::kExceptionTypeError, 0,
nullptr, 0);
// Copy the sessionId into a string so it can be captured for the lambda.
string sessionId(aSessionId, aSessionId + aSessionIdLength);
// Hold a reference to the SessionManager, so it isn't released before we // try and use it.
RefPtr<ClearKeySessionManager> self(this);
function<void()> deferrer = [self, aPromiseId, sessionId]() {
self->RemoveSession(aPromiseId, sessionId.data(), sessionId.size());
};
// If we haven't fully loaded, defer calling this method. if (MaybeDeferTillInitialized(std::move(deferrer))) {
CK_LOGD("Deferring RemoveSession"); return;
}
// Check that the SessionManager has not been shutdown before we try and // resolve any promises. if (!mHost) { return;
}
auto itr = mSessions.find(sessionId); if (itr == mSessions.end()) {
CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
Status status = Status::kSuccess; // According to the comment `If |iv_size| = 0, the data is unencrypted.` // Use iv_size to determine if the sample is encrypted. if (aBuffer.iv_size != 0) {
status = mDecryptionManager->Decrypt(buffer->Data(), buffer->Size(),
CryptoMetaData(&aBuffer));
}
void ClearKeySessionManager::OnQueryOutputProtectionStatus(
QueryResult aResult, uint32_t aLinkMask, uint32_t aOutputProtectionMask) {
MOZ_ASSERT(mHasOutstandingOutputProtectionQuery, "Should only be called if a query is outstanding");
CK_LOGD("ClearKeySessionManager::OnQueryOutputProtectionStatus");
mHasOutstandingOutputProtectionQuery = false;
if (aResult == QueryResult::kQueryFailed) { // Indicate the query failed. This can happen if we're in shutdown.
NotifyOutputProtectionStatus(KeyStatus::kInternalError); return;
}
if (aLinkMask & OutputLinkTypes::kLinkTypeNetwork) {
NotifyOutputProtectionStatus(KeyStatus::kOutputRestricted); return;
}
void ClearKeySessionManager::QueryOutputProtectionStatusIfNeeded() {
MOZ_ASSERT(
mHost, "Should not query protection status if we're shutdown (mHost == null)!");
CK_LOGD( "ClearKeySessionManager::UpdateOutputProtectionStatusAndQueryIfNeeded"); if (mLastOutputProtectionQueryTime.IsNull()) { // We haven't perfomed a check yet, get a query going.
MOZ_ASSERT(
!mHasOutstandingOutputProtectionQuery, "Shouldn't have an outstanding query if we haven't recorded a time");
QueryOutputProtectionStatusFromHost(); return;
}
MOZ_ASSERT(!mLastOutputProtectionQueryTime.IsNull(), "Should have already handled the case where we don't yet have a " "previous check time"); const mozilla::TimeStamp now = mozilla::TimeStamp::NowLoRes(); const mozilla::TimeDuration timeSinceQuery =
now - mLastOutputProtectionQueryTime;
// The time between output protection checks to the host. I.e. if this amount // of time has passed since the last check with the host, another should be // performed (provided the first check has been handled). staticconst mozilla::TimeDuration kOutputProtectionQueryInterval =
mozilla::TimeDuration::FromSeconds(0.2); // The number of kOutputProtectionQueryInterval intervals we can miss before // we decide a check has failed. I.e. if this value is 2, if we have not // received a reply to a check after kOutputProtectionQueryInterval * 2 // time, we consider the check failed.
constexpr uint32_t kMissedIntervalsBeforeFailure = 2; // The length of time after which we will restrict output until we get a // query response. staticconst mozilla::TimeDuration kTimeToWaitBeforeFailure =
kOutputProtectionQueryInterval * kMissedIntervalsBeforeFailure;
if ((timeSinceQuery > kOutputProtectionQueryInterval) &&
!mHasOutstandingOutputProtectionQuery) { // We don't have an outstanding query and enough time has passed we should // query again.
QueryOutputProtectionStatusFromHost(); return;
}
if ((timeSinceQuery > kTimeToWaitBeforeFailure) &&
mHasOutstandingOutputProtectionQuery) { // A reponse was not received fast enough, notify.
NotifyOutputProtectionStatus(KeyStatus::kInternalError);
}
}
void ClearKeySessionManager::QueryOutputProtectionStatusFromHost() {
MOZ_ASSERT(
mHost, "Should not query protection status if we're shutdown (mHost == null)!");
CK_LOGD("ClearKeySessionManager::QueryOutputProtectionStatusFromHost"); if (mHost) {
mLastOutputProtectionQueryTime = mozilla::TimeStamp::NowLoRes();
mHost->QueryOutputProtectionStatus();
mHasOutstandingOutputProtectionQuery = true;
}
}
void ClearKeySessionManager::NotifyOutputProtectionStatus(KeyStatus aStatus) {
MOZ_ASSERT(aStatus == KeyStatus::kUsable ||
aStatus == KeyStatus::kOutputRestricted ||
aStatus == KeyStatus::kInternalError, "aStatus should have an expected value");
CK_LOGD("ClearKeySessionManager::NotifyOutputProtectionStatus"); if (!mLastSessionId.has_value()) { // If we don't have a session id, either because we're too early, or are // shutting down, don't notify. return;
}
string& lastSessionId = mLastSessionId.value();
// Use 'output-protection' as the key ID. This helps tests disambiguate key // status updates related to this. const uint8_t kKeyId[] = {'o', 'u', 't', 'p', 'u', 't', '-', 'p', 'r', 'o', 't', 'e', 'c', 't', 'i', 'o', 'n'};
KeyInformation keyInfo = {};
keyInfo.key_id = kKeyId;
keyInfo.key_id_size = std::size(kKeyId);
keyInfo.status = aStatus;
// At time of writing, Gecko's higher level handling doesn't use this arg. // However, we set it to false to mimic Chromium's similar case. Since // Clearkey is used to test the Chromium CDM path, it doesn't hurt to try // and mimic their behaviour. bool hasAdditionalUseableKey = false;
mHost->OnSessionKeysChange(lastSessionId.c_str(), lastSessionId.size(),
hasAdditionalUseableKey, keyInfos.data(),
keyInfos.size());
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.