Bug 1111160 - Dispatch observer service notifications when content succeeds or fails to get CDM access. r=bz a=lmandel
authorChris Pearce <cpearce@mozilla.com>
Sat, 14 Feb 2015 08:52:42 +1300
changeset 250214 4c7cf01583e2
parent 250213 e3bf6bb9b33a
child 250215 e4eece82fbe1
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
bugs1111160
milestone37.0
Bug 1111160 - Dispatch observer service notifications when content succeeds or fails to get CDM access. r=bz a=lmandel
dom/base/Navigator.cpp
dom/media/eme/MediaKeyStatusMap.cpp
dom/media/eme/MediaKeySystemAccess.cpp
dom/media/eme/MediaKeySystemAccess.h
dom/media/eme/MediaKeys.cpp
dom/media/fmp4/MP4Reader.cpp
dom/media/gmp/GMPService.cpp
dom/webidl/MediaKeysRequestStatus.webidl
dom/webidl/moz.build
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2577,27 +2577,38 @@ Navigator::RequestMediaKeySystemAccess(c
 {
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   nsRefPtr<Promise> p = Promise::Create(go, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!Preferences::GetBool("media.eme.enabled", false)) {
+    // EME disabled by user, send notification to chrome so UI can
+    // inform user.
+    MediaKeySystemAccess::NotifyObservers(aKeySystem,
+                                          MediaKeySystemStatus::Api_disabled);
     p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return p.forget();
   }
 
   if (aKeySystem.IsEmpty() ||
       (aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
     p->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return p.forget();
   }
 
-  if (!MediaKeySystemAccess::IsKeySystemSupported(aKeySystem)) {
+  MediaKeySystemStatus status = MediaKeySystemAccess::GetKeySystemStatus(aKeySystem);
+  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(aKeySystem, 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())) {
--- a/dom/media/eme/MediaKeyStatusMap.cpp
+++ b/dom/media/eme/MediaKeyStatusMap.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/dom/MediaKeyStatusMap.h"
 #include "nsPIDOMWindow.h"
-#include "mozilla/dom/MediaKeyStatusMap.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/dom/ToJSValue.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeyStatusMap)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeyStatusMap)
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -13,16 +13,18 @@
 #endif
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #include "nsContentCID.h"
 #include "nsServiceManagerUtils.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "VideoUtils.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeySystemAccess,
                                       mParent)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
@@ -85,45 +87,58 @@ HaveGMPFor(mozIGeckoMediaPluginService* 
                                              &tags,
                                              &hasPlugin))) {
     return false;
   }
   return hasPlugin;
 }
 
 /* static */
-bool
-MediaKeySystemAccess::IsKeySystemSupported(const nsAString& aKeySystem)
+MediaKeySystemStatus
+MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem)
 {
+  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 false;
+    return MediaKeySystemStatus::Error;
   }
 
-  if (aKeySystem.EqualsLiteral("org.w3.clearkey") &&
-      HaveGMPFor(mps,
-                 NS_LITERAL_CSTRING("org.w3.clearkey"),
-                 NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
-    return true;
+  if (aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+    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;
   }
 
 #ifdef XP_WIN
   if ((aKeySystem.EqualsLiteral("com.adobe.access") ||
-       aKeySystem.EqualsLiteral("com.adobe.primetime")) &&
-      Preferences::GetBool("media.gmp-eme-adobe.enabled", false) &&
-      IsVistaOrLater() && // Win Vista and later only.
-      HaveGMPFor(mps,
-                 NS_ConvertUTF16toUTF8(aKeySystem),
-                 NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
-      return true;
+       aKeySystem.EqualsLiteral("com.adobe.primetime"))) {
+    // Win Vista and later only.
+    if (!IsVistaOrLater()) {
+      return MediaKeySystemStatus::Cdm_not_supported;
+    }
+    if (!Preferences::GetBool("media.eme.adobe-access.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;
   }
 #endif
 
-  return false;
+  return MediaKeySystemStatus::Cdm_not_supported;
 }
 
 static bool
 IsPlayableWithGMP(mozIGeckoMediaPluginService* aGMPS,
                   const nsAString& aKeySystem,
                   const nsAString& aContentType)
 {
 #ifdef MOZ_FMP4
@@ -204,10 +219,26 @@ MediaKeySystemAccess::IsSupported(const 
     // and on for specific GMPs/CDMs, so we don't check the uniqueidentifier
     // and stateful attributes here.
 
     return true;
   }
   return false;
 }
 
+/* static */
+void
+MediaKeySystemAccess::NotifyObservers(const nsAString& aKeySystem,
+                                      MediaKeySystemStatus aStatus)
+{
+  RequestMediaKeySystemAccessNotification data;
+  data.mKeySystem = aKeySystem;
+  data.mStatus = aStatus;
+  nsAutoString json;
+  data.ToJSON(json);
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (obs) {
+    obs->NotifyObservers(nullptr, "mediakeys-request", json.get());
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeySystemAccess.h
+++ b/dom/media/eme/MediaKeySystemAccess.h
@@ -9,16 +9,18 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaKeySystemAccessBinding.h"
+#include "mozilla/dom/MediaKeysRequestStatusBinding.h"
+
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace dom {
 
 class MediaKeySystemAccess MOZ_FINAL : public nsISupports,
                                        public nsWrapperCache
 {
@@ -37,21 +39,24 @@ public:
   nsPIDOMWindow* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   void GetKeySystem(nsString& aRetVal) const;
 
   already_AddRefed<Promise> CreateMediaKeys(ErrorResult& aRv);
 
-  static bool IsKeySystemSupported(const nsAString& aKeySystem);
+  static MediaKeySystemStatus GetKeySystemStatus(const nsAString& aKeySystem);
 
   static bool IsSupported(const nsAString& aKeySystem,
                           const Sequence<MediaKeySystemOptions>& aOptions);
 
+  static void NotifyObservers(const nsAString& aKeySystem,
+                              MediaKeySystemStatus aStatus);
+
 private:
   nsCOMPtr<nsPIDOMWindow> mParent;
   const nsString mKeySystem;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -22,16 +22,17 @@
 #include "MP4Decoder.h"
 #endif
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #include "nsContentCID.h"
 #include "nsServiceManagerUtils.h"
 #include "mozIGeckoMediaPluginService.h"
+#include "mozilla/dom/MediaKeySystemAccess.h"
 
 namespace mozilla {
 
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeys,
                                       mElement,
                                       mParent,
@@ -360,16 +361,19 @@ MediaKeys::OnCDMCreated(PromiseId aId, c
     return;
   }
   mNodeId = aNodeId;
   nsRefPtr<MediaKeys> keys(this);
   promise->MaybeResolve(keys);
   if (mCreatePromiseId == aId) {
     Release();
   }
+
+  MediaKeySystemAccess::NotifyObservers(mKeySystem,
+                                        MediaKeySystemStatus::Cdm_created);
 }
 
 already_AddRefed<MediaKeySession>
 MediaKeys::CreateSession(JSContext* aCx,
                          SessionType aSessionType,
                          ErrorResult& aRv)
 {
   nsRefPtr<MediaKeySession> session = new MediaKeySession(aCx,
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -374,24 +374,22 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo
   } else if (mPlatform && !IsWaitingMediaResources()) {
     *aInfo = mInfo;
     *aTags = nullptr;
     return NS_OK;
   }
 
   if (mDemuxer->Crypto().valid) {
 #ifdef MOZ_EME
-    if (!sIsEMEEnabled) {
-      // TODO: Need to signal DRM/EME required somehow...
-      return NS_ERROR_FAILURE;
-    }
-
     // We have encrypted audio or video. We'll need a CDM to decrypt and
     // possibly decode this. Wait until we've received a CDM from the
-    // JavaScript player app.
+    // JavaScript player app. Note: we still go through the motions here
+    // even if EME is disabled, so that if script tries and fails to create
+    // a CDM, we can detect that and notify chrome and show some UI explaining
+    // that we failed due to EME being disabled.
     nsRefPtr<CDMProxy> proxy;
     nsTArray<uint8_t> initData;
     ExtractCryptoInitData(initData);
     if (initData.Length() == 0) {
       return NS_ERROR_FAILURE;
     }
     if (!mInitDataEncountered.Contains(initData)) {
       mInitDataEncountered.AppendElement(initData);
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "GMPService.h"
 #include "prio.h"
 #include "prlog.h"
 #include "GMPParent.h"
@@ -177,16 +177,20 @@ GeckoMediaPluginService::Init()
 
   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsService);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "last-pb-context-exited", false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "browser:purge-session-history", false)));
 
+#ifdef DEBUG
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "mediakeys-request", false)));
+#endif
+
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     prefs->AddObserver("media.gmp.plugin.crash", this, false);
   }
 
 #ifndef MOZ_WIDGET_GONK
   // Directory service is main thread only, so cache the profile dir here
   // so that we can use it off main thread.
@@ -213,17 +217,18 @@ GeckoMediaPluginService::Init()
   return GetThread(getter_AddRefs(thread));
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginService::Observe(nsISupports* aSubject,
                                  const char* aTopic,
                                  const char16_t* aSomeData)
 {
-  LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, aTopic));
+  LOGD(("%s::%s topic='%s' data='%s'", __CLASS__, __FUNCTION__,
+       aTopic, NS_ConvertUTF16toUTF8(aSomeData).get()));
   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
     if (branch) {
       bool crashNow = false;
       if (NS_LITERAL_STRING("media.gmp.plugin.crash").Equals(aSomeData)) {
         branch->GetBoolPref("media.gmp.plugin.crash",  &crashNow);
       }
       if (crashNow) {
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MediaKeysRequestStatus.webidl
@@ -0,0 +1,20 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+enum MediaKeySystemStatus {
+  "available",
+  "api-disabled",
+  "cdm-disabled",
+  "cdm-not-supported",
+  "cdm-not-installed",
+  "cdm-created",
+  "error"
+};
+
+dictionary RequestMediaKeySystemAccessNotification {
+  required DOMString keySystem;
+  required MediaKeySystemStatus status;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -797,11 +797,12 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser'
 
 if CONFIG['MOZ_EME']:
     WEBIDL_FILES += [
         'MediaEncryptedEvent.webidl',
         'MediaKeyError.webidl',
         'MediaKeyMessageEvent.webidl',
         'MediaKeys.webidl',
         'MediaKeySession.webidl',
+        'MediaKeysRequestStatus.webidl',
         'MediaKeyStatusMap.webidl',
         'MediaKeySystemAccess.webidl',
     ]