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

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             : * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "MediaKeySystemAccessManager.h"
       6             : #include "DecoderDoctorDiagnostics.h"
       7             : #include "MediaPrefs.h"
       8             : #include "mozilla/EMEUtils.h"
       9             : #include "nsServiceManagerUtils.h"
      10             : #include "nsComponentManagerUtils.h"
      11             : #include "nsIObserverService.h"
      12             : #include "mozilla/Services.h"
      13             : #include "mozilla/DetailedPromise.h"
      14             : #ifdef XP_WIN
      15             : #include "mozilla/WindowsVersion.h"
      16             : #endif
      17             : #ifdef XP_MACOSX
      18             : #include "nsCocoaFeatures.h"
      19             : #endif
      20             : #include "nsPrintfCString.h"
      21             : #include "nsContentUtils.h"
      22             : #include "nsIScriptError.h"
      23             : #include "mozilla/Unused.h"
      24             : #include "nsDataHashtable.h"
      25             : 
      26             : namespace mozilla {
      27             : namespace dom {
      28             : 
      29           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccessManager)
      30           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
      31           0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
      32           0 : NS_INTERFACE_MAP_END
      33             : 
      34           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccessManager)
      35           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccessManager)
      36             : 
      37             : NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeySystemAccessManager)
      38             : 
      39           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaKeySystemAccessManager)
      40           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
      41           0 :   for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
      42           0 :     tmp->mRequests[i].RejectPromise(NS_LITERAL_CSTRING("Promise still outstanding at MediaKeySystemAccessManager GC"));
      43           0 :     tmp->mRequests[i].CancelTimer();
      44           0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequests[i].mPromise)
      45             :   }
      46           0 :   tmp->mRequests.Clear();
      47           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      48             : 
      49           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaKeySystemAccessManager)
      50           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
      51           0 :   for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
      52           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequests[i].mPromise)
      53             :   }
      54           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      55             : 
      56           0 : MediaKeySystemAccessManager::MediaKeySystemAccessManager(nsPIDOMWindowInner* aWindow)
      57             :   : mWindow(aWindow)
      58           0 :   , mAddedObservers(false)
      59             : {
      60           0 : }
      61             : 
      62           0 : MediaKeySystemAccessManager::~MediaKeySystemAccessManager()
      63             : {
      64           0 :   Shutdown();
      65           0 : }
      66             : 
      67             : void
      68           0 : MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
      69             :                                      const nsAString& aKeySystem,
      70             :                                      const Sequence<MediaKeySystemConfiguration>& aConfigs)
      71             : {
      72           0 :   Request(aPromise, aKeySystem, aConfigs, RequestType::Initial);
      73           0 : }
      74             : 
      75             : void
      76           0 : MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
      77             :                                      const nsAString& aKeySystem,
      78             :                                      const Sequence<MediaKeySystemConfiguration>& aConfigs,
      79             :                                      RequestType aType)
      80             : {
      81           0 :   EME_LOG("MediaKeySystemAccessManager::Request %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
      82             : 
      83           0 :   if (aKeySystem.IsEmpty()) {
      84           0 :     aPromise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      85           0 :                           NS_LITERAL_CSTRING("Key system string is empty"));
      86             :     // Don't notify DecoderDoctor, as there's nothing we or the user can
      87             :     // do to fix this situation; the site is using the API wrong.
      88           0 :     return;
      89             :   }
      90           0 :   if (aConfigs.IsEmpty()) {
      91           0 :     aPromise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
      92           0 :                           NS_LITERAL_CSTRING("Candidate MediaKeySystemConfigs is empty"));
      93             :     // Don't notify DecoderDoctor, as there's nothing we or the user can
      94             :     // do to fix this situation; the site is using the API wrong.
      95           0 :     return;
      96             :   }
      97             : 
      98           0 :   DecoderDoctorDiagnostics diagnostics;
      99             : 
     100             :   // Ensure keysystem is supported.
     101           0 :   if (!IsWidevineKeySystem(aKeySystem) && !IsClearkeyKeySystem(aKeySystem)) {
     102             :     // Not to inform user, because nothing to do if the keySystem is not
     103             :     // supported.
     104           0 :     aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
     105           0 :                           NS_LITERAL_CSTRING("Key system is unsupported"));
     106           0 :     diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
     107           0 :                                           aKeySystem, false, __func__);
     108           0 :     return;
     109             :   }
     110             : 
     111           0 :   if (!MediaPrefs::EMEEnabled() && !IsClearkeyKeySystem(aKeySystem)) {
     112             :     // EME disabled by user, send notification to chrome so UI can inform user.
     113             :     // Clearkey is allowed even when EME is disabled because we want the pref
     114             :     // "media.eme.enabled" only taking effect on proprietary DRMs.
     115           0 :     MediaKeySystemAccess::NotifyObservers(mWindow,
     116             :                                           aKeySystem,
     117           0 :                                           MediaKeySystemStatus::Api_disabled);
     118           0 :     aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
     119           0 :                           NS_LITERAL_CSTRING("EME has been preffed off"));
     120           0 :     diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
     121           0 :                                           aKeySystem, false, __func__);
     122           0 :     return;
     123             :   }
     124             : 
     125           0 :   nsAutoCString message;
     126             :   MediaKeySystemStatus status =
     127           0 :     MediaKeySystemAccess::GetKeySystemStatus(aKeySystem, message);
     128             : 
     129             :   nsPrintfCString msg("MediaKeySystemAccess::GetKeySystemStatus(%s) "
     130             :                       "result=%s msg='%s'",
     131           0 :                       NS_ConvertUTF16toUTF8(aKeySystem).get(),
     132           0 :                       MediaKeySystemStatusValues::strings[(size_t)status].value,
     133           0 :                       message.get());
     134           0 :   LogToBrowserConsole(NS_ConvertUTF8toUTF16(msg));
     135             : 
     136           0 :   if (status == MediaKeySystemStatus::Cdm_not_installed &&
     137           0 :       IsWidevineKeySystem(aKeySystem)) {
     138             :     // These are cases which could be resolved by downloading a new(er) CDM.
     139             :     // When we send the status to chrome, chrome's GMPProvider will attempt to
     140             :     // download or update the CDM. In AwaitInstall() we add listeners to wait
     141             :     // for the update to complete, and we'll call this function again with
     142             :     // aType==Subsequent once the download has completed and the GMPService
     143             :     // has had a new plugin added. AwaitInstall() sets a timer to fail if the
     144             :     // update/download takes too long or fails.
     145           0 :     if (aType == RequestType::Initial &&
     146           0 :         AwaitInstall(aPromise, aKeySystem, aConfigs)) {
     147             :       // Notify chrome that we're going to wait for the CDM to download/update.
     148             :       // Note: If we're re-trying, we don't re-send the notification,
     149             :       // as chrome is already displaying the "we can't play, updating"
     150             :       // notification.
     151           0 :       MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, status);
     152             :     } else {
     153             :       // We waited or can't wait for an update and we still can't service
     154             :       // the request. Give up. Chrome will still be showing a "I can't play,
     155             :       // updating" notification.
     156           0 :       aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
     157           0 :                             NS_LITERAL_CSTRING("Gave up while waiting for a CDM update"));
     158             :     }
     159           0 :     diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
     160           0 :                                           aKeySystem, false, __func__);
     161           0 :     return;
     162             :   }
     163           0 :   if (status != MediaKeySystemStatus::Available) {
     164             :     // Failed due to user disabling something, send a notification to
     165             :     // chrome, so we can show some UI to explain how the user can rectify
     166             :     // the situation.
     167           0 :     MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, status);
     168           0 :     aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR, message);
     169           0 :     return;
     170             :   }
     171             : 
     172           0 :   nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
     173           0 :   nsDataHashtable<nsCharPtrHashKey, bool> warnings;
     174             :   std::function<void(const char*)> deprecationWarningLogFn =
     175           0 :     [&](const char* aMsgName) {
     176           0 :       EME_LOG("Logging deprecation warning '%s' to WebConsole.", aMsgName);
     177           0 :       warnings.Put(aMsgName, true);
     178           0 :       nsString uri;
     179           0 :       if (doc) {
     180           0 :         Unused << doc->GetDocumentURI(uri);
     181             :       }
     182           0 :       const char16_t* params[] = { uri.get() };
     183           0 :       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
     184           0 :                                       NS_LITERAL_CSTRING("Media"),
     185             :                                       doc,
     186             :                                       nsContentUtils::eDOM_PROPERTIES,
     187             :                                       aMsgName,
     188             :                                       params,
     189           0 :                                       ArrayLength(params));
     190           0 :     };
     191             : 
     192             :   bool isPrivateBrowsing =
     193           0 :     mWindow->GetExtantDoc() &&
     194           0 :     mWindow->GetExtantDoc()->NodePrincipal()->GetPrivateBrowsingId() > 0;
     195           0 :   MediaKeySystemConfiguration config;
     196           0 :   if (MediaKeySystemAccess::GetSupportedConfig(
     197             :         aKeySystem, aConfigs, config, &diagnostics, isPrivateBrowsing, deprecationWarningLogFn)) {
     198             :     RefPtr<MediaKeySystemAccess> access(
     199           0 :       new MediaKeySystemAccess(mWindow, aKeySystem, config));
     200           0 :     aPromise->MaybeResolve(access);
     201           0 :     diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
     202           0 :                                           aKeySystem, true, __func__);
     203             : 
     204             :     // Accumulate telemetry to report whether we hit deprecation warnings.
     205           0 :     if (warnings.Get("MediaEMENoCapabilitiesDeprecatedWarning")) {
     206             :       Telemetry::Accumulate(
     207           0 :         Telemetry::HistogramID::MEDIA_EME_REQUEST_DEPRECATED_WARNINGS, 1);
     208           0 :       EME_LOG("MEDIA_EME_REQUEST_DEPRECATED_WARNINGS "
     209             :               "MediaEMENoCapabilitiesDeprecatedWarning");
     210           0 :     } else if (warnings.Get("MediaEMENoCodecsDeprecatedWarning")) {
     211             :       Telemetry::Accumulate(
     212           0 :         Telemetry::HistogramID::MEDIA_EME_REQUEST_DEPRECATED_WARNINGS, 2);
     213           0 :       EME_LOG("MEDIA_EME_REQUEST_DEPRECATED_WARNINGS "
     214             :               "MediaEMENoCodecsDeprecatedWarning");
     215             :     } else {
     216             :       Telemetry::Accumulate(
     217           0 :         Telemetry::HistogramID::MEDIA_EME_REQUEST_DEPRECATED_WARNINGS, 0);
     218           0 :       EME_LOG("MEDIA_EME_REQUEST_DEPRECATED_WARNINGS No warnings");
     219             :     }
     220           0 :     return;
     221             :   }
     222             :   // Not to inform user, because nothing to do if the corresponding keySystem
     223             :   // configuration is not supported.
     224           0 :   aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
     225           0 :                         NS_LITERAL_CSTRING("Key system configuration is not supported"));
     226           0 :   diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
     227           0 :                                         aKeySystem, false, __func__);
     228             : }
     229             : 
     230           0 : MediaKeySystemAccessManager::PendingRequest::PendingRequest(DetailedPromise* aPromise,
     231             :                                                             const nsAString& aKeySystem,
     232             :                                                             const Sequence<MediaKeySystemConfiguration>& aConfigs,
     233           0 :                                                             nsITimer* aTimer)
     234             :   : mPromise(aPromise)
     235             :   , mKeySystem(aKeySystem)
     236             :   , mConfigs(aConfigs)
     237           0 :   , mTimer(aTimer)
     238             : {
     239           0 :   MOZ_COUNT_CTOR(MediaKeySystemAccessManager::PendingRequest);
     240           0 : }
     241             : 
     242           0 : MediaKeySystemAccessManager::PendingRequest::PendingRequest(const PendingRequest& aOther)
     243             :   : mPromise(aOther.mPromise)
     244             :   , mKeySystem(aOther.mKeySystem)
     245             :   , mConfigs(aOther.mConfigs)
     246           0 :   , mTimer(aOther.mTimer)
     247             : {
     248           0 :   MOZ_COUNT_CTOR(MediaKeySystemAccessManager::PendingRequest);
     249           0 : }
     250             : 
     251           0 : MediaKeySystemAccessManager::PendingRequest::~PendingRequest()
     252             : {
     253           0 :   MOZ_COUNT_DTOR(MediaKeySystemAccessManager::PendingRequest);
     254           0 : }
     255             : 
     256             : void
     257           0 : MediaKeySystemAccessManager::PendingRequest::CancelTimer()
     258             : {
     259           0 :   if (mTimer) {
     260           0 :     mTimer->Cancel();
     261             :   }
     262           0 : }
     263             : 
     264             : void
     265           0 : MediaKeySystemAccessManager::PendingRequest::RejectPromise(const nsCString& aReason)
     266             : {
     267           0 :   if (mPromise) {
     268           0 :     mPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR, aReason);
     269             :   }
     270           0 : }
     271             : 
     272             : bool
     273           0 : MediaKeySystemAccessManager::AwaitInstall(DetailedPromise* aPromise,
     274             :                                           const nsAString& aKeySystem,
     275             :                                           const Sequence<MediaKeySystemConfiguration>& aConfigs)
     276             : {
     277           0 :   EME_LOG("MediaKeySystemAccessManager::AwaitInstall %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
     278             : 
     279           0 :   if (!EnsureObserversAdded()) {
     280           0 :     NS_WARNING("Failed to add pref observer");
     281           0 :     return false;
     282             :   }
     283             : 
     284           0 :   nsCOMPtr<nsITimer> timer(do_CreateInstance("@mozilla.org/timer;1"));
     285           0 :   if (!timer || NS_FAILED(timer->Init(this, 60 * 1000, nsITimer::TYPE_ONE_SHOT))) {
     286           0 :     NS_WARNING("Failed to create timer to await CDM install.");
     287           0 :     return false;
     288             :   }
     289             : 
     290           0 :   mRequests.AppendElement(PendingRequest(aPromise, aKeySystem, aConfigs, timer));
     291           0 :   return true;
     292             : }
     293             : 
     294             : void
     295           0 : MediaKeySystemAccessManager::RetryRequest(PendingRequest& aRequest)
     296             : {
     297           0 :   aRequest.CancelTimer();
     298           0 :   Request(aRequest.mPromise, aRequest.mKeySystem, aRequest.mConfigs, RequestType::Subsequent);
     299           0 : }
     300             : 
     301             : nsresult
     302           0 : MediaKeySystemAccessManager::Observe(nsISupports* aSubject,
     303             :                                      const char* aTopic,
     304             :                                      const char16_t* aData)
     305             : {
     306           0 :   EME_LOG("MediaKeySystemAccessManager::Observe %s", aTopic);
     307             : 
     308           0 :   if (!strcmp(aTopic, "gmp-changed")) {
     309             :     // Filter out the requests where the CDM's install-status is no longer
     310             :     // "unavailable". This will be the CDMs which have downloaded since the
     311             :     // initial request.
     312             :     // Note: We don't have a way to communicate from chrome that the CDM has
     313             :     // failed to download, so we'll just let the timeout fail us in that case.
     314           0 :     nsTArray<PendingRequest> requests;
     315           0 :     for (size_t i = mRequests.Length(); i-- > 0; ) {
     316           0 :       PendingRequest& request = mRequests[i];
     317           0 :       nsAutoCString message;
     318             :       MediaKeySystemStatus status =
     319           0 :         MediaKeySystemAccess::GetKeySystemStatus(request.mKeySystem, message);
     320           0 :       if (status == MediaKeySystemStatus::Cdm_not_installed) {
     321             :         // Not yet installed, don't retry. Keep waiting until timeout.
     322           0 :         continue;
     323             :       }
     324             :       // Status has changed, retry request.
     325           0 :       requests.AppendElement(Move(request));
     326           0 :       mRequests.RemoveElementAt(i);
     327             :     }
     328             :     // Retry all pending requests, but this time fail if the CDM is not installed.
     329           0 :     for (PendingRequest& request : requests) {
     330           0 :       RetryRequest(request);
     331             :     }
     332           0 :   } else if (!strcmp(aTopic, "timer-callback")) {
     333             :     // Find the timer that expired and re-run the request for it.
     334           0 :     nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject));
     335           0 :     for (size_t i = 0; i < mRequests.Length(); i++) {
     336           0 :       if (mRequests[i].mTimer == timer) {
     337           0 :         EME_LOG("MediaKeySystemAccessManager::AwaitInstall resuming request");
     338           0 :         PendingRequest request = mRequests[i];
     339           0 :         mRequests.RemoveElementAt(i);
     340           0 :         RetryRequest(request);
     341           0 :         break;
     342             :       }
     343             :     }
     344             :   }
     345           0 :   return NS_OK;
     346             : }
     347             : 
     348             : bool
     349           0 : MediaKeySystemAccessManager::EnsureObserversAdded()
     350             : {
     351           0 :   if (mAddedObservers) {
     352           0 :     return true;
     353             :   }
     354             : 
     355           0 :   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
     356           0 :   if (NS_WARN_IF(!obsService)) {
     357           0 :     return false;
     358             :   }
     359           0 :   mAddedObservers = NS_SUCCEEDED(obsService->AddObserver(this, "gmp-changed", false));
     360           0 :   return mAddedObservers;
     361             : }
     362             : 
     363             : void
     364           0 : MediaKeySystemAccessManager::Shutdown()
     365             : {
     366           0 :   EME_LOG("MediaKeySystemAccessManager::Shutdown");
     367           0 :   nsTArray<PendingRequest> requests(Move(mRequests));
     368           0 :   for (PendingRequest& request : requests) {
     369             :     // Cancel all requests; we're shutting down.
     370           0 :     request.CancelTimer();
     371           0 :     request.RejectPromise(NS_LITERAL_CSTRING("Promise still outstanding at MediaKeySystemAccessManager shutdown"));
     372             :   }
     373           0 :   if (mAddedObservers) {
     374           0 :     nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
     375           0 :     if (obsService) {
     376           0 :       obsService->RemoveObserver(this, "gmp-changed");
     377           0 :       mAddedObservers = false;
     378             :     }
     379             :   }
     380           0 : }
     381             : 
     382             : } // namespace dom
     383             : } // namespace mozilla

Generated by: LCOV version 1.13