Bug 1124031 part 4 - Enforce min CDM version from keySystem string. r=bz
authorChris Pearce <cpearce@mozilla.com>
Fri, 20 Feb 2015 14:38:08 +1300
changeset 229956 138f7daa8b624c2375b855407125ab39f214c40b
parent 229955 b37b1cc5163fb89cf3f346f51fae6b09f604a67e
child 229957 a71a4b10000ab73c33eec3b0eacfe53da1325763
push id55851
push usercpearce@mozilla.com
push dateFri, 20 Feb 2015 01:38:33 +0000
treeherdermozilla-inbound@138f7daa8b62 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1124031
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1124031 part 4 - Enforce min CDM version from keySystem string. r=bz
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;
 
@@ -2632,33 +2636,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