mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
340
dom/media/eme/MediaKeySystemAccessManager.cpp
Normal file
340
dom/media/eme/MediaKeySystemAccessManager.cpp
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MediaKeySystemAccessManager.h"
|
||||
#include "DecoderDoctorDiagnostics.h"
|
||||
#include "MediaPrefs.h"
|
||||
#include "mozilla/EMEUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/DetailedPromise.h"
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
#include "nsCocoaFeatures.h"
|
||||
#endif
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccessManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccessManager)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccessManager)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeySystemAccessManager)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaKeySystemAccessManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
|
||||
tmp->mRequests[i].RejectPromise(NS_LITERAL_CSTRING("Promise still outstanding at MediaKeySystemAccessManager GC"));
|
||||
tmp->mRequests[i].CancelTimer();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequests[i].mPromise)
|
||||
}
|
||||
tmp->mRequests.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaKeySystemAccessManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequests[i].mPromise)
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
MediaKeySystemAccessManager::MediaKeySystemAccessManager(nsPIDOMWindowInner* aWindow)
|
||||
: mWindow(aWindow)
|
||||
, mAddedObservers(false)
|
||||
{
|
||||
}
|
||||
|
||||
MediaKeySystemAccessManager::~MediaKeySystemAccessManager()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
|
||||
const nsAString& aKeySystem,
|
||||
const Sequence<MediaKeySystemConfiguration>& aConfigs)
|
||||
{
|
||||
Request(aPromise, aKeySystem, aConfigs, RequestType::Initial);
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
|
||||
const nsAString& aKeySystem,
|
||||
const Sequence<MediaKeySystemConfiguration>& aConfigs,
|
||||
RequestType aType)
|
||||
{
|
||||
EME_LOG("MediaKeySystemAccessManager::Request %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
|
||||
|
||||
if (aKeySystem.IsEmpty()) {
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
|
||||
NS_LITERAL_CSTRING("Key system string is empty"));
|
||||
// Don't notify DecoderDoctor, as there's nothing we or the user can
|
||||
// do to fix this situation; the site is using the API wrong.
|
||||
return;
|
||||
}
|
||||
if (aConfigs.IsEmpty()) {
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
|
||||
NS_LITERAL_CSTRING("Candidate MediaKeySystemConfigs is empty"));
|
||||
// Don't notify DecoderDoctor, as there's nothing we or the user can
|
||||
// do to fix this situation; the site is using the API wrong.
|
||||
return;
|
||||
}
|
||||
|
||||
DecoderDoctorDiagnostics diagnostics;
|
||||
|
||||
// Ensure keysystem is supported.
|
||||
if (!IsWidevineKeySystem(aKeySystem) &&
|
||||
!IsClearkeyKeySystem(aKeySystem) &&
|
||||
!IsPrimetimeKeySystem(aKeySystem)) {
|
||||
// Not to inform user, because nothing to do if the keySystem is not
|
||||
// supported.
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
NS_LITERAL_CSTRING("Key system is unsupported"));
|
||||
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
|
||||
aKeySystem, false, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MediaPrefs::EMEEnabled() && !IsClearkeyKeySystem(aKeySystem)) {
|
||||
// EME disabled by user, send notification to chrome so UI can inform user.
|
||||
// Clearkey is allowed even when EME is disabled because we want the pref
|
||||
// "media.eme.enabled" only taking effect on proprietary DRMs.
|
||||
MediaKeySystemAccess::NotifyObservers(mWindow,
|
||||
aKeySystem,
|
||||
MediaKeySystemStatus::Api_disabled);
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
NS_LITERAL_CSTRING("EME has been preffed off"));
|
||||
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
|
||||
aKeySystem, false, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString message;
|
||||
MediaKeySystemStatus status =
|
||||
MediaKeySystemAccess::GetKeySystemStatus(aKeySystem, message);
|
||||
|
||||
nsPrintfCString msg("MediaKeySystemAccess::GetKeySystemStatus(%s) "
|
||||
"result=%s msg='%s'",
|
||||
NS_ConvertUTF16toUTF8(aKeySystem).get(),
|
||||
MediaKeySystemStatusValues::strings[(size_t)status].value,
|
||||
message.get());
|
||||
LogToBrowserConsole(NS_ConvertUTF8toUTF16(msg));
|
||||
|
||||
if (status == MediaKeySystemStatus::Cdm_not_installed &&
|
||||
(IsPrimetimeKeySystem(aKeySystem) || IsWidevineKeySystem(aKeySystem))) {
|
||||
// These are cases which could be resolved by downloading a new(er) CDM.
|
||||
// When we send the status to chrome, chrome's GMPProvider will attempt to
|
||||
// download or update the CDM. In AwaitInstall() we add listeners to wait
|
||||
// for the update to complete, and we'll call this function again with
|
||||
// aType==Subsequent once the download has completed and the GMPService
|
||||
// has had a new plugin added. AwaitInstall() sets a timer to fail if the
|
||||
// update/download takes too long or fails.
|
||||
if (aType == RequestType::Initial &&
|
||||
AwaitInstall(aPromise, aKeySystem, aConfigs)) {
|
||||
// Notify chrome that we're going to wait for the CDM to download/update.
|
||||
// Note: If we're re-trying, we don't re-send the notification,
|
||||
// as chrome is already displaying the "we can't play, updating"
|
||||
// notification.
|
||||
MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, status);
|
||||
} else {
|
||||
// We waited or can't wait for an update and we still can't service
|
||||
// the request. Give up. Chrome will still be showing a "I can't play,
|
||||
// updating" notification.
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
NS_LITERAL_CSTRING("Gave up while waiting for a CDM update"));
|
||||
}
|
||||
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
|
||||
aKeySystem, false, __func__);
|
||||
return;
|
||||
}
|
||||
if (status != MediaKeySystemStatus::Available) {
|
||||
// Failed due to user disabling something, send a notification to
|
||||
// chrome, so we can show some UI to explain how the user can rectify
|
||||
// the situation.
|
||||
MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, status);
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR, message);
|
||||
return;
|
||||
}
|
||||
|
||||
MediaKeySystemConfiguration config;
|
||||
if (MediaKeySystemAccess::GetSupportedConfig(aKeySystem, aConfigs, config, &diagnostics)) {
|
||||
RefPtr<MediaKeySystemAccess> access(
|
||||
new MediaKeySystemAccess(mWindow, aKeySystem, config));
|
||||
aPromise->MaybeResolve(access);
|
||||
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
|
||||
aKeySystem, true, __func__);
|
||||
return;
|
||||
}
|
||||
// Not to inform user, because nothing to do if the corresponding keySystem
|
||||
// configuration is not supported.
|
||||
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
|
||||
NS_LITERAL_CSTRING("Key system configuration is not supported"));
|
||||
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
|
||||
aKeySystem, false, __func__);
|
||||
}
|
||||
|
||||
MediaKeySystemAccessManager::PendingRequest::PendingRequest(DetailedPromise* aPromise,
|
||||
const nsAString& aKeySystem,
|
||||
const Sequence<MediaKeySystemConfiguration>& aConfigs,
|
||||
nsITimer* aTimer)
|
||||
: mPromise(aPromise)
|
||||
, mKeySystem(aKeySystem)
|
||||
, mConfigs(aConfigs)
|
||||
, mTimer(aTimer)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaKeySystemAccessManager::PendingRequest);
|
||||
}
|
||||
|
||||
MediaKeySystemAccessManager::PendingRequest::PendingRequest(const PendingRequest& aOther)
|
||||
: mPromise(aOther.mPromise)
|
||||
, mKeySystem(aOther.mKeySystem)
|
||||
, mConfigs(aOther.mConfigs)
|
||||
, mTimer(aOther.mTimer)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaKeySystemAccessManager::PendingRequest);
|
||||
}
|
||||
|
||||
MediaKeySystemAccessManager::PendingRequest::~PendingRequest()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MediaKeySystemAccessManager::PendingRequest);
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeySystemAccessManager::PendingRequest::CancelTimer()
|
||||
{
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeySystemAccessManager::PendingRequest::RejectPromise(const nsCString& aReason)
|
||||
{
|
||||
if (mPromise) {
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR, aReason);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaKeySystemAccessManager::AwaitInstall(DetailedPromise* aPromise,
|
||||
const nsAString& aKeySystem,
|
||||
const Sequence<MediaKeySystemConfiguration>& aConfigs)
|
||||
{
|
||||
EME_LOG("MediaKeySystemAccessManager::AwaitInstall %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
|
||||
|
||||
if (!EnsureObserversAdded()) {
|
||||
NS_WARNING("Failed to add pref observer");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITimer> timer(do_CreateInstance("@mozilla.org/timer;1"));
|
||||
if (!timer || NS_FAILED(timer->Init(this, 60 * 1000, nsITimer::TYPE_ONE_SHOT))) {
|
||||
NS_WARNING("Failed to create timer to await CDM install.");
|
||||
return false;
|
||||
}
|
||||
|
||||
mRequests.AppendElement(PendingRequest(aPromise, aKeySystem, aConfigs, timer));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeySystemAccessManager::RetryRequest(PendingRequest& aRequest)
|
||||
{
|
||||
aRequest.CancelTimer();
|
||||
Request(aRequest.mPromise, aRequest.mKeySystem, aRequest.mConfigs, RequestType::Subsequent);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaKeySystemAccessManager::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
EME_LOG("MediaKeySystemAccessManager::Observe %s", aTopic);
|
||||
|
||||
if (!strcmp(aTopic, "gmp-changed")) {
|
||||
// Filter out the requests where the CDM's install-status is no longer
|
||||
// "unavailable". This will be the CDMs which have downloaded since the
|
||||
// initial request.
|
||||
// Note: We don't have a way to communicate from chrome that the CDM has
|
||||
// failed to download, so we'll just let the timeout fail us in that case.
|
||||
nsTArray<PendingRequest> requests;
|
||||
for (size_t i = mRequests.Length(); i-- > 0; ) {
|
||||
PendingRequest& request = mRequests[i];
|
||||
nsAutoCString message;
|
||||
MediaKeySystemStatus status =
|
||||
MediaKeySystemAccess::GetKeySystemStatus(request.mKeySystem, message);
|
||||
if (status == MediaKeySystemStatus::Cdm_not_installed) {
|
||||
// Not yet installed, don't retry. Keep waiting until timeout.
|
||||
continue;
|
||||
}
|
||||
// Status has changed, retry request.
|
||||
requests.AppendElement(Move(request));
|
||||
mRequests.RemoveElementAt(i);
|
||||
}
|
||||
// Retry all pending requests, but this time fail if the CDM is not installed.
|
||||
for (PendingRequest& request : requests) {
|
||||
RetryRequest(request);
|
||||
}
|
||||
} else if (!strcmp(aTopic, "timer-callback")) {
|
||||
// Find the timer that expired and re-run the request for it.
|
||||
nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject));
|
||||
for (size_t i = 0; i < mRequests.Length(); i++) {
|
||||
if (mRequests[i].mTimer == timer) {
|
||||
EME_LOG("MediaKeySystemAccessManager::AwaitInstall resuming request");
|
||||
PendingRequest request = mRequests[i];
|
||||
mRequests.RemoveElementAt(i);
|
||||
RetryRequest(request);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaKeySystemAccessManager::EnsureObserversAdded()
|
||||
{
|
||||
if (mAddedObservers) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
|
||||
if (NS_WARN_IF(!obsService)) {
|
||||
return false;
|
||||
}
|
||||
mAddedObservers = NS_SUCCEEDED(obsService->AddObserver(this, "gmp-changed", false));
|
||||
return mAddedObservers;
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeySystemAccessManager::Shutdown()
|
||||
{
|
||||
EME_LOG("MediaKeySystemAccessManager::Shutdown");
|
||||
nsTArray<PendingRequest> requests(Move(mRequests));
|
||||
for (PendingRequest& request : requests) {
|
||||
// Cancel all requests; we're shutting down.
|
||||
request.CancelTimer();
|
||||
request.RejectPromise(NS_LITERAL_CSTRING("Promise still outstanding at MediaKeySystemAccessManager shutdown"));
|
||||
}
|
||||
if (mAddedObservers) {
|
||||
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
|
||||
if (obsService) {
|
||||
obsService->RemoveObserver(this, "gmp-changed");
|
||||
mAddedObservers = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
Loading…
Add table
Add a link
Reference in a new issue