Bug 1124031 part 4 - Enforce min CDM version from keySystem string. r=bz a=lmandel
authorChris Pearce <cpearce@mozilla.com>
Fri, 20 Feb 2015 14:38:08 +1300
changeset 250223 6437b406a0fa
parent 250222 16dddf827464
child 250224 d56acccf3b69
push id4521
push usercpearce@mozilla.com
push date2015-03-04 01:22 +0000
treeherdermozilla-beta@8abdbdecd2d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, lmandel
bugs1124031
milestone37.0
Bug 1124031 part 4 - Enforce min CDM version from keySystem string. r=bz a=lmandel
dom/base/Navigator.cpp
dom/media/eme/MediaKeySystemAccess.cpp
dom/media/eme/MediaKeySystemAccess.h
dom/media/test/test_eme_requestKeySystemAccess.html
dom/webidl/MediaKeysRequestStatus.webidl
media/gmp-clearkey/0.1/clearkey.info.in
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -107,16 +107,20 @@
 
 #if defined(XP_LINUX)
 #include "mozilla/Hal.h"
 #endif
 #include "mozilla/dom/ContentChild.h"
 
 #include "mozilla/dom/FeatureList.h"
 
+#ifdef MOZ_EME
+#include "mozilla/EMEUtils.h"
+#endif
+
 namespace mozilla {
 namespace dom {
 
 static bool sDoNotTrackEnabled = false;
 static bool sVibratorEnabled   = false;
 static uint32_t sMaxVibrateMS  = 0;
 static uint32_t sMaxVibrateListLen = 0;
 
@@ -2591,33 +2595,46 @@ Navigator::RequestMediaKeySystemAccess(c
   }
 
   if (aKeySystem.IsEmpty() ||
       (aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
     p->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return p.forget();
   }
 
-  MediaKeySystemStatus status = MediaKeySystemAccess::GetKeySystemStatus(aKeySystem);
+  // Parse keysystem, split it out into keySystem prefix, and version suffix.
+  nsAutoString keySystem;
+  int32_t minCdmVersion = NO_CDM_VERSION;
+  if (!ParseKeySystem(aKeySystem,
+                      keySystem,
+                      minCdmVersion)) {
+    // Invalid keySystem string, or unsupported keySystem. Send notification
+    // to chrome to show a failure notice.
+    MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, MediaKeySystemStatus::Cdm_not_supported);
+    p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return p.forget();
+  }
+
+  MediaKeySystemStatus status = MediaKeySystemAccess::GetKeySystemStatus(keySystem, minCdmVersion);
   if (status != MediaKeySystemStatus::Available) {
     if (status != MediaKeySystemStatus::Error) {
       // 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);
+      MediaKeySystemAccess::NotifyObservers(mWindow, keySystem, status);
     }
     p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return p.forget();
   }
 
   // TODO: Wait (async) until the CDM is downloaded, if it's not already.
 
   if (!aOptions.WasPassed() ||
-      MediaKeySystemAccess::IsSupported(aKeySystem, aOptions.Value())) {
-    nsRefPtr<MediaKeySystemAccess> access(new MediaKeySystemAccess(mWindow, aKeySystem));
+      MediaKeySystemAccess::IsSupported(keySystem, aOptions.Value())) {
+    nsRefPtr<MediaKeySystemAccess> access(new MediaKeySystemAccess(mWindow, keySystem));
     p->MaybeResolve(access);
     return p.forget();
   }
 
   p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 
   return p.forget();
 }
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -76,30 +76,56 @@ HaveGMPFor(mozIGeckoMediaPluginService* 
            const nsCString& aAPI,
            const nsCString& aTag = EmptyCString())
 {
   nsTArray<nsCString> tags;
   tags.AppendElement(aKeySystem);
   if (!aTag.IsEmpty()) {
     tags.AppendElement(aTag);
   }
-  // Note: EME plugins need a non-null nodeId here, as they must
-  // not be shared across origins.
   bool hasPlugin = false;
   if (NS_FAILED(aGMPService->HasPluginForAPI(aAPI,
                                              &tags,
                                              &hasPlugin))) {
     return false;
   }
   return hasPlugin;
 }
 
+static MediaKeySystemStatus
+EnsureMinCDMVersion(mozIGeckoMediaPluginService* aGMPService,
+                    const nsAString& aKeySystem,
+                    int32_t aMinCdmVersion)
+{
+  if (aMinCdmVersion == NO_CDM_VERSION) {
+    return MediaKeySystemStatus::Available;
+  }
+
+  nsTArray<nsCString> tags;
+  tags.AppendElement(NS_ConvertUTF16toUTF8(aKeySystem));
+  nsAutoCString versionStr;
+  if (NS_FAILED(aGMPService->GetPluginVersionForAPI(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
+                                                    &tags,
+                                                    versionStr))) {
+    return MediaKeySystemStatus::Error;
+  }
+
+  nsresult rv;
+  int32_t version = versionStr.ToInteger(&rv);
+  if (NS_FAILED(rv) || version < 0 || aMinCdmVersion > version) {
+    return MediaKeySystemStatus::Cdm_insufficient_version;
+  }
+
+  return MediaKeySystemStatus::Available;
+}
+
 /* static */
 MediaKeySystemStatus
-MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem)
+MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem,
+                                         int32_t aMinCdmVersion)
 {
   MOZ_ASSERT(Preferences::GetBool("media.eme.enabled", false));  
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (NS_WARN_IF(!mps)) {
     return MediaKeySystemStatus::Error;
   }
 
@@ -107,17 +133,17 @@ MediaKeySystemAccess::GetKeySystemStatus
     if (!Preferences::GetBool("media.eme.clearkey.enabled", true)) {
       return MediaKeySystemStatus::Cdm_disabled;
     }
     if (!HaveGMPFor(mps,
                     NS_LITERAL_CSTRING("org.w3.clearkey"),
                     NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
       return MediaKeySystemStatus::Cdm_not_installed;
     }
-    return MediaKeySystemStatus::Available;
+    return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion);
   }
 
 #ifdef XP_WIN
   if ((aKeySystem.EqualsLiteral("com.adobe.access") ||
        aKeySystem.EqualsLiteral("com.adobe.primetime"))) {
     // Win Vista and later only.
     if (!IsVistaOrLater()) {
       return MediaKeySystemStatus::Cdm_not_supported;
@@ -125,17 +151,17 @@ MediaKeySystemAccess::GetKeySystemStatus
     if (!Preferences::GetBool("media.gmp-eme-adobe.enabled", false)) {
       return MediaKeySystemStatus::Cdm_disabled;
     }
     if (!HaveGMPFor(mps,
                     NS_ConvertUTF16toUTF8(aKeySystem),
                     NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
       return MediaKeySystemStatus::Cdm_not_installed;
     }
-    return MediaKeySystemStatus::Available;
+    return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion);
   }
 #endif
 
   return MediaKeySystemStatus::Cdm_not_supported;
 }
 
 static bool
 IsPlayableWithGMP(mozIGeckoMediaPluginService* aGMPS,
--- a/dom/media/eme/MediaKeySystemAccess.h
+++ b/dom/media/eme/MediaKeySystemAccess.h
@@ -39,17 +39,18 @@ public:
   nsPIDOMWindow* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   void GetKeySystem(nsString& aRetVal) const;
 
   already_AddRefed<Promise> CreateMediaKeys(ErrorResult& aRv);
 
-  static MediaKeySystemStatus GetKeySystemStatus(const nsAString& aKeySystem);
+  static MediaKeySystemStatus GetKeySystemStatus(const nsAString& aKeySystem,
+                                                 int32_t aMinCdmVersion);
 
   static bool IsSupported(const nsAString& aKeySystem,
                           const Sequence<MediaKeySystemOptions>& aOptions);
 
   static void NotifyObservers(nsIDOMWindow* aWindow,
                               const nsAString& aKeySystem,
                               MediaKeySystemStatus aStatus);
 
--- a/dom/media/test/test_eme_requestKeySystemAccess.html
+++ b/dom/media/test/test_eme_requestKeySystemAccess.html
@@ -261,16 +261,36 @@ var tests = [
     options: [
       {
         initDataType: 'webm',
         videoType: 'video/webm',
       }
     ],
     shouldPass: false,
   },
+  {
+    name: "CDM version less than",
+    keySystem: CLEARKEY_ID + ".0",
+    shouldPass: true
+  },
+  {
+    name: "CDM version equal to",
+    keySystem: CLEARKEY_ID + ".1",
+    shouldPass: true
+  },
+  {
+    name: "CDM version greater than",
+    keySystem: CLEARKEY_ID + ".2",
+    shouldPass: false
+  },
+  {
+    name: "Non-whole number CDM version",
+    keySystem: CLEARKEY_ID + ".0.1",
+    shouldPass: false
+  },
 ];
 
 function beginTest() {
   Promise.all(tests.map(Test)).then(function() { SimpleTest.finish(); });
 }
 
 var prefs = [
   [ "media.mediasource.enabled", true ],
--- a/dom/webidl/MediaKeysRequestStatus.webidl
+++ b/dom/webidl/MediaKeysRequestStatus.webidl
@@ -5,16 +5,17 @@
  */
 
 enum MediaKeySystemStatus {
   "available",
   "api-disabled",
   "cdm-disabled",
   "cdm-not-supported",
   "cdm-not-installed",
+  "cdm-insufficient-version",
   "cdm-created",
   "error"
 };
 
 dictionary RequestMediaKeySystemAccessNotification {
   required DOMString keySystem;
   required MediaKeySystemStatus status;
 };
--- a/media/gmp-clearkey/0.1/clearkey.info.in
+++ b/media/gmp-clearkey/0.1/clearkey.info.in
@@ -1,10 +1,10 @@
 Name: clearkey
-Description: ClearKey decrypt-only GMP plugin
-Version: 0.1
+Description: ClearKey Gecko Media Plugin
+Version: 1
 #ifdef ENABLE_WMF
 APIs: eme-decrypt-v6[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey]
 Libraries: dxva2.dll, d3d9.dll, msmpeg2vdec.dll, msmpeg2adec.dll, MSAudDecMFT.dll
 #else
 APIs: eme-decrypt-v6[org.w3.clearkey]
 Libraries:
 #endif