Bug 1095257 - Implement Navigator.requestMediaKeySystemAccess(). r=edwin r=bz r=peterv
authorChris Pearce <cpearce@mozilla.com>
Tue, 18 Nov 2014 22:13:02 +1300
changeset 216354 40711e8b703dfcf6b6b4ed75838297322a13a8a0
parent 216353 ec82fb8c912775ef3dfdfc11992faee876d5ff15
child 216355 32819579175213132b4971aa05c1601cddaef8ca
push id27849
push usercbook@mozilla.com
push dateWed, 19 Nov 2014 12:36:08 +0000
treeherdermozilla-central@07ad59382922 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin, bz, peterv
bugs1095257
milestone36.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 1095257 - Implement Navigator.requestMediaKeySystemAccess(). r=edwin r=bz r=peterv
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/media/DecoderTraits.cpp
dom/media/eme/MediaKeySystemAccess.cpp
dom/media/eme/MediaKeySystemAccess.h
dom/media/eme/MediaKeys.cpp
dom/media/eme/MediaKeys.h
dom/media/eme/moz.build
dom/media/fmp4/MP4Decoder.cpp
dom/media/fmp4/MP4Decoder.h
dom/media/test/eme.js
dom/media/test/mochitest.ini
dom/media/test/test_eme_playback.html
dom/media/test/test_eme_requestKeySystemAccess.html
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/MediaKeySystemAccess.webidl
dom/webidl/MediaKeys.webidl
dom/webidl/Navigator.webidl
dom/webidl/moz.build
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2528,10 +2528,48 @@ Navigator::GetUserAgent(nsPIDOMWindow* a
     do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
   if (!siteSpecificUA) {
     return NS_OK;
   }
 
   return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
 }
 
+#ifdef MOZ_EME
+already_AddRefed<Promise>
+Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
+                                       const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
+                                       ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
+  nsRefPtr<Promise> p = Promise::Create(go, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  if (aKeySystem.IsEmpty() ||
+      (aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
+    p->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return p.forget();
+  }
+
+  if (!MediaKeySystemAccess::IsKeySystemSupported(aKeySystem)) {
+    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));
+    p->MaybeResolve(access);
+    return p.forget();
+  }
+
+  p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+
+  return p.forget();
+}
+#endif
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -13,16 +13,19 @@
 #include "nsIDOMNavigator.h"
 #include "nsIMozNavigatorNetwork.h"
 #include "nsAutoPtr.h"
 #include "nsWrapperCache.h"
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
+#ifdef MOZ_EME
+#include "mozilla/dom/MediaKeySystemAccess.h"
+#endif
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsPIDOMWindow;
 class nsIDOMNavigatorSystemMessages;
 class nsDOMCameraManager;
 class nsDOMDeviceStorage;
 class nsIPrincipal;
@@ -311,16 +314,23 @@ public:
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetWindow();
   }
 
   virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
 
+#ifdef MOZ_EME
+  already_AddRefed<Promise>
+  RequestMediaKeySystemAccess(const nsAString& aKeySystem,
+                              const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
+                              ErrorResult& aRv);
+#endif
+
 private:
   virtual ~Navigator();
 
   bool CheckPermission(const char* type);
   static bool CheckPermission(nsPIDOMWindow* aWindow, const char* aType);
   // GetWindowFromGlobal returns the inner window for this global, if
   // any, else null.
   static already_AddRefed<nsPIDOMWindow> GetWindowFromGlobal(JSObject* aGlobal);
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -330,18 +330,19 @@ IsMP4SupportedType(const nsACString& aTy
                    const nsAString& aCodecs = EmptyString())
 {
 // Currently on B2G, FMP4 is only working for MSE playback.
 // For other normal MP4, it still uses current omx decoder.
 // Bug 1061034 is a follow-up bug to enable all MP4s with MOZ_FMP4
 #ifdef MOZ_OMX_DECODER
   return false;
 #else
+  bool haveAAC, haveMP3, haveH264;
   return Preferences::GetBool("media.fragmented-mp4.exposed", false) &&
-         MP4Decoder::CanHandleMediaType(aType, aCodecs);
+         MP4Decoder::CanHandleMediaType(aType, aCodecs, haveAAC, haveH264, haveMP3);
 #endif
 }
 #endif
 
 #ifdef MOZ_APPLEMEDIA
 static const char * const gAppleMP3Types[] = {
   "audio/mp3",
   "audio/mpeg",
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/MediaKeySystemAccess.h"
+#include "mozilla/dom/MediaKeySystemAccessBinding.h"
+#include "mozilla/Preferences.h"
+#include "nsContentTypeParser.h"
+#ifdef MOZ_FMP4
+#include "MP4Decoder.h"
+#endif
+#ifdef XP_WIN
+#include "mozilla/WindowsVersion.h"
+#endif
+#include "nsContentCID.h"
+#include "nsServiceManagerUtils.h"
+#include "mozIGeckoMediaPluginService.h"
+#include "VideoUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeySystemAccess,
+                                      mParent)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccess)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+MediaKeySystemAccess::MediaKeySystemAccess(nsPIDOMWindow* aParent,
+                                           const nsAString& aKeySystem)
+  : mParent(aParent)
+  , mKeySystem(aKeySystem)
+{
+}
+
+MediaKeySystemAccess::~MediaKeySystemAccess()
+{
+}
+
+JSObject*
+MediaKeySystemAccess::WrapObject(JSContext* aCx)
+{
+  return MediaKeySystemAccessBinding::Wrap(aCx, this);
+}
+
+nsPIDOMWindow*
+MediaKeySystemAccess::GetParentObject() const
+{
+  return mParent;
+}
+
+void
+MediaKeySystemAccess::GetKeySystem(nsString& aRetVal) const
+{
+  aRetVal = mKeySystem;
+}
+
+already_AddRefed<Promise>
+MediaKeySystemAccess::CreateMediaKeys(ErrorResult& aRv)
+{
+  nsRefPtr<MediaKeys> keys(new MediaKeys(mParent, mKeySystem));
+  return keys->Init(aRv);
+}
+
+static bool
+HaveGMPFor(mozIGeckoMediaPluginService* aGMPService,
+           const nsCString& aKeySystem,
+           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 */
+bool
+MediaKeySystemAccess::IsKeySystemSupported(const nsAString& aKeySystem)
+{
+  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  if (NS_WARN_IF(!mps)) {
+    return false;
+  }
+
+  if (aKeySystem.EqualsLiteral("org.w3.clearkey") &&
+      HaveGMPFor(mps,
+                 NS_LITERAL_CSTRING("org.w3.clearkey"),
+                 NS_LITERAL_CSTRING("eme-decrypt"))) {
+    return true;
+  }
+
+#ifdef XP_WIN
+  if (aKeySystem.EqualsLiteral("com.adobe.access") &&
+      Preferences::GetBool("media.eme.adobe-access.enabled", false) &&
+      IsVistaOrLater() && // Win Vista and later only.
+      HaveGMPFor(mps,
+                 NS_LITERAL_CSTRING("com.adobe.access"),
+                 NS_LITERAL_CSTRING("eme-decrypt"))) {
+      return true;
+  }
+#endif
+
+  return false;
+}
+
+static bool
+IsPlayableWithGMP(mozIGeckoMediaPluginService* aGMPS,
+                  const nsAString& aKeySystem,
+                  const nsAString& aContentType)
+{
+#ifdef MOZ_FMP4
+  nsContentTypeParser parser(aContentType);
+  nsAutoString mimeType;
+  nsresult rv = parser.GetType(mimeType);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  if (!mimeType.EqualsLiteral("audio/mp4") &&
+      !mimeType.EqualsLiteral("audio/x-m4a") &&
+      !mimeType.EqualsLiteral("video/mp4")) {
+    return false;
+  }
+
+  nsAutoString codecs;
+  parser.GetParameter("codecs", codecs);
+
+  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
+  bool hasAAC = false;
+  bool hasH264 = false;
+  bool hasMP3 = false;
+  if (!MP4Decoder::CanHandleMediaType(mimeTypeUTF8,
+                                      codecs,
+                                      hasAAC,
+                                      hasH264,
+                                      hasMP3) ||
+      hasMP3) {
+    return false;
+  }
+  return (!hasAAC || !HaveGMPFor(aGMPS,
+                                 NS_ConvertUTF16toUTF8(aKeySystem),
+                                 NS_LITERAL_CSTRING("eme-decrypt"),
+                                 NS_LITERAL_CSTRING("aac"))) &&
+         (!hasH264 || !HaveGMPFor(aGMPS,
+                                  NS_ConvertUTF16toUTF8(aKeySystem),
+                                  NS_LITERAL_CSTRING("eme-decrypt"),
+                                  NS_LITERAL_CSTRING("h264")));
+#else
+  return false;
+#endif
+}
+
+/* static */
+bool
+MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
+                                  const Sequence<MediaKeySystemOptions>& aOptions)
+{
+  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  if (NS_WARN_IF(!mps)) {
+    return false;
+  }
+
+  for (size_t i = 0; i < aOptions.Length(); i++) {
+    const MediaKeySystemOptions& options = aOptions[i];
+    if (!options.mInitDataType.EqualsLiteral("cenc")) {
+      continue;
+    }
+    if (!options.mAudioCapability.IsEmpty() ||
+        !options.mVideoCapability.IsEmpty()) {
+      // Don't support any capabilites until we know we have a CDM with
+      // capabilities...
+      continue;
+    }
+    if (!options.mAudioType.IsEmpty() &&
+        !IsPlayableWithGMP(mps, aKeySystem, options.mAudioType)) {
+      continue;
+    }
+    if (!options.mVideoType.IsEmpty() &&
+        !IsPlayableWithGMP(mps, aKeySystem, options.mVideoType)) {
+      continue;
+    }
+
+    // Our sandbox provides an origin specific unique identifier, and the
+    // ability to persist data. We don't yet have a way to turn those off
+    // and on for specific GMPs/CDMs, so we don't check the uniqueidentifier
+    // and stateful attributes here.
+
+    return true;
+  }
+  return false;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/MediaKeySystemAccess.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_MediaKeySystemAccess_h
+#define mozilla_dom_MediaKeySystemAccess_h
+
+#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 "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class MediaKeySystemAccess MOZ_FINAL : public nsISupports,
+                                       public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeySystemAccess)
+
+public:
+  explicit MediaKeySystemAccess(nsPIDOMWindow* aParent,
+                                const nsAString& aKeySystem);
+
+protected:
+  ~MediaKeySystemAccess();
+
+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 bool IsSupported(const nsAString& aKeySystem,
+                          const Sequence<MediaKeySystemOptions>& aOptions);
+
+private:
+  nsCOMPtr<nsPIDOMWindow> mParent;
+  const nsString mKeySystem;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MediaKeySystemAccess_h
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -109,121 +109,16 @@ MediaKeys::SetServerCertificate(const Ar
     promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return promise.forget();
   }
 
   mProxy->SetServerCertificate(StorePromise(promise), data);
   return promise.forget();
 }
 
-static bool
-HaveGMPFor(const nsCString& aKeySystem,
-           const nsCString& aAPI,
-           const nsCString& aTag = EmptyCString())
-{
-  nsCOMPtr<mozIGeckoMediaPluginService> mps =
-    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
-  if (NS_WARN_IF(!mps)) {
-    return false;
-  }
-
-  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(mps->HasPluginForAPI(aAPI,
-                                     &tags,
-                                     &hasPlugin))) {
-    return false;
-  }
-  return hasPlugin;
-}
-
-static bool
-IsPlayableMP4Type(const nsAString& aContentType)
-{
-#ifdef MOZ_FMP4
-  nsContentTypeParser parser(aContentType);
-  nsAutoString mimeType;
-  nsresult rv = parser.GetType(mimeType);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-  nsAutoString codecs;
-  parser.GetParameter("codecs", codecs);
-
-  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
-  return MP4Decoder::CanHandleMediaType(mimeTypeUTF8, codecs);
-#else
-  return false;
-#endif
-}
-
-bool
-MediaKeys::IsTypeSupported(const nsAString& aKeySystem,
-                           const Optional<nsAString>& aInitDataType,
-                           const Optional<nsAString>& aContentType)
-{
-  if (aKeySystem.EqualsLiteral("org.w3.clearkey") &&
-      (!aInitDataType.WasPassed() || aInitDataType.Value().EqualsLiteral("cenc")) &&
-      (!aContentType.WasPassed() || IsPlayableMP4Type(aContentType.Value())) &&
-      HaveGMPFor(NS_LITERAL_CSTRING("org.w3.clearkey"),
-                 NS_LITERAL_CSTRING("eme-decrypt"))) {
-    return true;
-  }
-
-#ifdef XP_WIN
-  // Note: Adobe Access's GMP uses WMF to decode, so anything our MP4Reader
-  // thinks it can play on Windows, the Access GMP should be able to play.
-  if (aKeySystem.EqualsLiteral("com.adobe.access") &&
-      Preferences::GetBool("media.eme.adobe-access.enabled", false) &&
-      IsVistaOrLater() && // Win Vista and later only.
-      (!aInitDataType.WasPassed() || aInitDataType.Value().EqualsLiteral("cenc")) &&
-      (!aContentType.WasPassed() || IsPlayableMP4Type(aContentType.Value())) &&
-      HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
-                 NS_LITERAL_CSTRING("eme-decrypt")) &&
-      HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
-                 NS_LITERAL_CSTRING("decode-video"),
-                 NS_LITERAL_CSTRING("h264")) &&
-      HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
-                 NS_LITERAL_CSTRING("decode-audio"),
-                 NS_LITERAL_CSTRING("aac"))) {
-      return true;
-  }
-#endif
-
-  return false;
-}
-
-/* static */
-IsTypeSupportedResult
-MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
-                           const nsAString& aKeySystem,
-                           const Optional<nsAString>& aInitDataType,
-                           const Optional<nsAString>& aContentType,
-                           const Optional<nsAString>& aCapability)
-{
-  // TODO: Should really get spec changed to this is async, so we can wait
-  //       for user to consent to running plugin.
-  bool supported = IsTypeSupported(aKeySystem, aInitDataType, aContentType);
-
-  EME_LOG("MediaKeys::IsTypeSupported keySystem='%s' initDataType='%s' contentType='%s' supported=%d",
-          NS_ConvertUTF16toUTF8(aKeySystem).get(),
-          (aInitDataType.WasPassed() ? NS_ConvertUTF16toUTF8(aInitDataType.Value()).get() : ""),
-          (aContentType.WasPassed() ? NS_ConvertUTF16toUTF8(aContentType.Value()).get() : ""),
-          supported);
-
-  return supported ? IsTypeSupportedResult::Probably
-                   : IsTypeSupportedResult::_empty;
-}
-
 already_AddRefed<Promise>
 MediaKeys::MakePromise(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     NS_WARNING("Passed non-global to MediaKeys ctor!");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
@@ -298,47 +193,24 @@ MediaKeys::ResolvePromise(PromiseId aId)
     mPendingSessions.Remove(aId);
     mKeySessions.Put(session->GetSessionId(), session);
     promise->MaybeResolve(session);
   } else {
     promise->MaybeResolve(JS::UndefinedHandleValue);
   }
 }
 
-/* static */
-already_AddRefed<Promise>
-MediaKeys::Create(const GlobalObject& aGlobal,
-                  const nsAString& aKeySystem,
-                  ErrorResult& aRv)
-{
-  // CDMProxy keeps MediaKeys alive until it resolves the promise and thus
-  // returns the MediaKeys object to JS.
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  if (!window || !window->GetExtantDoc()) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  nsRefPtr<MediaKeys> keys = new MediaKeys(window, aKeySystem);
-  return keys->Init(aRv);
-}
-
 already_AddRefed<Promise>
 MediaKeys::Init(ErrorResult& aRv)
 {
   nsRefPtr<Promise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  if (!IsTypeSupported(mKeySystem)) {
-    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return promise.forget();
-  }
-
   mProxy = new CDMProxy(this, mKeySystem);
 
   // Determine principal (at creation time) of the MediaKeys object.
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
   if (!sop) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -48,16 +48,18 @@ class MediaKeys MOZ_FINAL : public nsISu
   ~MediaKeys();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
 
   MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem);
 
+  already_AddRefed<Promise> Init(ErrorResult& aRv);
+
   nsPIDOMWindow* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   nsresult Bind(HTMLMediaElement* aElement);
 
   // Javascript: readonly attribute DOMString keySystem;
   void GetKeySystem(nsString& retval) const;
@@ -65,29 +67,16 @@ public:
   // JavaScript: MediaKeys.createSession()
   already_AddRefed<MediaKeySession> CreateSession(SessionType aSessionType,
                                                   ErrorResult& aRv);
 
   // JavaScript: MediaKeys.SetServerCertificate()
   already_AddRefed<Promise> SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
                                                  ErrorResult& aRv);
 
-  // JavaScript: MediaKeys.create()
-  static
-  already_AddRefed<Promise> Create(const GlobalObject& aGlobal,
-                                   const nsAString& aKeySystem,
-                                   ErrorResult& aRv);
-
-  // JavaScript: MediaKeys.IsTypeSupported()
-  static IsTypeSupportedResult IsTypeSupported(const GlobalObject& aGlobal,
-                                               const nsAString& aKeySystem,
-                                               const Optional<nsAString>& aInitDataType,
-                                               const Optional<nsAString>& aContentType,
-                                               const Optional<nsAString>& aCapability);
-
   already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
 
   // Called once a Create() operation succeeds.
   void OnCDMCreated(PromiseId aId, const nsACString& aNodeId);
   // Called when GenerateRequest or Load have been called on a MediaKeySession
   // and we are waiting for its initialisation to finish.
   void OnSessionPending(PromiseId aId, MediaKeySession* aSession);
   // Called once a CreateSession succeeds.
@@ -123,22 +112,17 @@ public:
   nsresult CheckPrincipals();
 
   // Returns a pointer to the bound media element's owner doc.
   // If we're not bound, this returns null.
   nsIDocument* GetOwnerDoc() const;
 
 private:
 
-  static bool IsTypeSupported(const nsAString& aKeySystem,
-                              const Optional<nsAString>& aInitDataType = Optional<nsAString>(),
-                              const Optional<nsAString>& aContentType = Optional<nsAString>());
-
   bool IsInPrivateBrowsing();
-  already_AddRefed<Promise> Init(ErrorResult& aRv);
 
   // Removes promise from mPromises, and returns it.
   already_AddRefed<Promise> RetrievePromise(PromiseId aId);
 
   // Owning ref to proxy. The proxy has a weak reference back to the MediaKeys,
   // and the MediaKeys destructor clears the proxy's reference to the MediaKeys.
   nsRefPtr<CDMProxy> mProxy;
 
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
     'MediaEncryptedEvent.h',
     'MediaKeyError.h',
     'MediaKeyMessageEvent.h',
     'MediaKeys.h',
     'MediaKeySession.h',
+    'MediaKeySystemAccess.h',
 ]
 
 EXPORTS.mozilla += [
     'CDMCallbackProxy.h',
     'CDMCaps.h',
     'CDMProxy.h',
     'EMELog.h'
 ]
@@ -24,13 +25,14 @@ UNIFIED_SOURCES += [
     'CDMCaps.cpp',
     'CDMProxy.cpp',
     'EMELog.cpp',
     'MediaEncryptedEvent.cpp',
     'MediaKeyError.cpp',
     'MediaKeyMessageEvent.cpp',
     'MediaKeys.cpp',
     'MediaKeySession.cpp',
+    'MediaKeySystemAccess.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 FAIL_ON_WARNINGS = True
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -49,24 +49,35 @@ MP4Decoder::SetCDMProxy(CDMProxy* aProxy
       NS_NewRunnableMethod(this, &MediaDecoder::NotifyWaitingForResourcesStatusChanged));
     caps.CallOnMainThreadWhenCapsAvailable(task);
   }
   return NS_OK;
 }
 #endif
 
 static bool
-IsSupportedAudioCodec(const nsAString& aCodec)
+IsSupportedAudioCodec(const nsAString& aCodec,
+                      bool& aOutContainsAAC,
+                      bool& aOutContainsMP3)
 {
-  // AAC-LC, HE-AAC or MP3 in M4A.
-  return aCodec.EqualsASCII("mp4a.40.2") ||
+  // AAC-LC or HE-AAC in M4A.
+  aOutContainsAAC = aCodec.EqualsASCII("mp4a.40.2") ||
+                    aCodec.EqualsASCII("mp4a.40.5");
+  if (aOutContainsAAC) {
+    return true;
+  }
 #ifndef MOZ_GONK_MEDIACODEC // B2G doesn't support MP3 in MP4 yet.
-         aCodec.EqualsASCII("mp3") ||
+  aOutContainsMP3 = aCodec.EqualsASCII("mp3");
+  if (aOutContainsMP3) {
+    return true;
+  }
+#else
+  aOutContainsMP3 = false;
 #endif
-         aCodec.EqualsASCII("mp4a.40.5");
+  return false;
 }
 
 static bool
 IsSupportedH264Codec(const nsAString& aCodec)
 {
   int16_t profile = 0, level = 0;
 
   if (!ExtractH264CodecDetails(aCodec, profile, level)) {
@@ -87,48 +98,60 @@ IsSupportedH264Codec(const nsAString& aC
           profile == H264_PROFILE_MAIN ||
           profile == H264_PROFILE_EXTENDED ||
           profile == H264_PROFILE_HIGH);
 }
 
 /* static */
 bool
 MP4Decoder::CanHandleMediaType(const nsACString& aType,
-                               const nsAString& aCodecs)
+                               const nsAString& aCodecs,
+                               bool& aOutContainsAAC,
+                               bool& aOutContainsH264,
+                               bool& aOutContainsMP3)
 {
   if (!IsEnabled()) {
     return false;
   }
 
   if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
-    return aCodecs.IsEmpty() || IsSupportedAudioCodec(aCodecs);
+    return aCodecs.IsEmpty() ||
+           IsSupportedAudioCodec(aCodecs,
+                                 aOutContainsAAC,
+                                 aOutContainsMP3);
   }
 
   if (!aType.EqualsASCII("video/mp4")) {
     return false;
   }
 
   // Verify that all the codecs specifed are ones that we expect that
   // we can play.
   nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
   bool expectMoreTokens = false;
   while (tokenizer.hasMoreTokens()) {
     const nsSubstring& token = tokenizer.nextToken();
     expectMoreTokens = tokenizer.separatorAfterCurrentToken();
-    if (IsSupportedAudioCodec(token) || IsSupportedH264Codec(token)) {
+    if (IsSupportedAudioCodec(token,
+                              aOutContainsAAC,
+                              aOutContainsMP3)) {
+      continue;
+    }
+    if (IsSupportedH264Codec(token)) {
+      aOutContainsH264 = true;
       continue;
     }
     return false;
   }
   if (expectMoreTokens) {
     // Last codec name was empty
     return false;
   }
+
   return true;
-
 }
 
 static bool
 IsFFmpegAvailable()
 {
 #ifndef MOZ_FFMPEG
   return false;
 #else
--- a/dom/media/fmp4/MP4Decoder.h
+++ b/dom/media/fmp4/MP4Decoder.h
@@ -25,19 +25,23 @@ public:
   virtual MediaDecoderStateMachine* CreateStateMachine();
 
 #ifdef MOZ_EME
   virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
 #endif
 
   // Returns true if aMIMEType is a type that we think we can render with the
   // a MP4 platform decoder backend. If aCodecs is non emtpy, it is filled
-  // with a comma-delimited list of codecs to check support for.
+  // with a comma-delimited list of codecs to check support for. Notes in
+  // out params wether the codecs string contains AAC or H.264.
   static bool CanHandleMediaType(const nsACString& aMIMEType,
-                                 const nsAString& aCodecs = EmptyString());
+                                 const nsAString& aCodecs,
+                                 bool& aOutContainsAAC,
+                                 bool& aOutContainsH264,
+                                 bool& aOutContainsMP3);
 
   // Returns true if the MP4 backend is preffed on, and we're running on a
   // platform that is likely to have decoders for the contained formats.
   static bool IsEnabled();
 };
 
 } // namespace mozilla
 
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -187,28 +187,42 @@ function SetupEME(test, token, params)
   });
 
   var onSetKeysFail = (params && params.onSetKeysFail)
     ? params.onSetKeysFail
     : bail(token + " Failed to set MediaKeys on <video> element");
 
   v.addEventListener("encrypted", function(ev) {
     Log(token, "got encrypted event");
-    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
-      Log(token, "created MediaKeys object ok");
-      mediaKeys.sessions = [];
-      return v.setMediaKeys(mediaKeys);
-    }, bail("failed to create MediaKeys object")).then(function() {
-      Log(token, "set MediaKeys on <video> element ok");
+    var options = [
+      {
+        initDataType: ev.initDataType,
+        videoType: test.type,
+      }
+    ];
+    navigator.requestMediaKeySystemAccess(KEYSYSTEM_TYPE, options)
+      .then(function(keySystemAccess) {
+        return keySystemAccess.createMediaKeys();
+      }, bail(token + " Failed to request key system access."))
+      
+      .then(function(mediaKeys) {
+        Log(token, "created MediaKeys object ok");
+        mediaKeys.sessions = [];
+        return v.setMediaKeys(mediaKeys);
+      }, bail("failed to create MediaKeys object"))
+      
+      .then(function() {
+        Log(token, "set MediaKeys on <video> element ok");
 
-      var session = v.mediaKeys.createSession(test.sessionType);
-      if (params && params.onsessioncreated) {
-        params.onsessioncreated(session);
-      }
-      session.addEventListener("message", UpdateSessionFunc(test, token));
-      session.generateRequest(ev.initDataType, ev.initData).then(function() {
-      }, bail(token + " Failed to initialise MediaKeySession"));
-
-    }, onSetKeysFail);
+        var session = v.mediaKeys.createSession(test.sessionType);
+        if (params && params.onsessioncreated) {
+          params.onsessioncreated(session);
+        }
+        session.addEventListener("message", UpdateSessionFunc(test, token));
+        return session.generateRequest(ev.initDataType, ev.initData);
+      }, onSetKeysFail)
+      
+      .then(function() {
+        Log(token, "generated request");
+      }, bail(token + " Failed to request key system access2."));
   });
-
   return v;
 }
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -359,16 +359,18 @@ skip-if = (toolkit == 'android' && proce
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_eme_canvas_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_playback.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_requestKeySystemAccess.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_stream_capture_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_empty_resource.html]
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
--- a/dom/media/test/test_eme_playback.html
+++ b/dom/media/test/test_eme_playback.html
@@ -46,18 +46,16 @@ function startTest(test, token)
       }
     }
   );
 
   var gotEncrypted = false;
   var gotPlaying = false;
 
   v.addEventListener("encrypted", function(ev) {
-    ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
-       TimeStamp(token) + " MediaKeys should support this keysystem");
     gotEncrypted = true;
   });
 
   v.addEventListener("playing", function () { gotPlaying = true; });
 
   v.addEventListener("ended", function(ev) {
     ok(true, TimeStamp(token) + " got ended event");
 
@@ -83,33 +81,17 @@ function startTest(test, token)
     manager.finished(token);
    });
 
   v.addEventListener("error", bail(token + " got error event"));
 
   LoadTest(test, v, token).then(function(){v.play();}, bail(token + " failed to load"));
 }
 
-function testIsTypeSupported()
-{
-  var t = MediaKeys.isTypeSupported;
-  const clearkey = "org.w3.clearkey";
-  ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
-  ok(t(clearkey), "ClearKey supported.");
-  ok(!t(clearkey, "bogus"), "ClearKey bogus initDataType not supported.");
-  ok(t(clearkey, "cenc"), "ClearKey/cenc should be supported.");
-  ok(!t(clearkey, "cenc", "bogus"), "ClearKey/cenc bogus content type should be supported.");
-  ok(t(clearkey, "cenc", 'video/mp4'), "ClearKey/cenc video/mp4 supported.");
-  ok(t(clearkey, "cenc", 'video/mp4; codecs="avc1.4d4015,mp4a.40.2"'), "ClearKey/cenc H.264/AAC supported.");
-  ok(t(clearkey, "cenc", 'audio/mp4'), "ClearKey/cenc audio/mp4 supported.");
-  ok(t(clearkey, "cenc", 'audio/mp4; codecs="mp4a.40.2"'), "ClearKey/cenc AAC LC supported.");
-}
-
 function beginTest() {
-  testIsTypeSupported();
   manager.runTests(gEMETests, startTest);
 }
 
 var prefs = [
   [ "media.mediasource.enabled", true ],
   [ "media.mediasource.mp4.enabled", true ],
 ];
 
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_requestKeySystemAccess.html
@@ -0,0 +1,293 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function Test(test) {
+  var name = "'" + test.name + "'";
+  return new Promise(function(resolve, reject) {
+    var p;
+    if (test.options) {
+      p = navigator.requestMediaKeySystemAccess(test.keySystem, test.options);
+    } else {
+      p = navigator.requestMediaKeySystemAccess(test.keySystem);
+    }
+    p.then(
+      function(keySystemAccess) {
+        ok(test.shouldPass, name + " passed and was expected to " + (test.shouldPass ? "pass" : "fail"));
+        resolve();
+      },
+      function(ex) {
+        if (test.shouldPass) {
+          info(name + " failed: " + ex);
+        }
+        ok(!test.shouldPass, name + " failed and was expected to " + (test.shouldPass ? "pass" : "fail"));
+        resolve();
+      });
+  });
+}
+
+const CLEARKEY_ID = 'org.w3.clearkey';
+
+var tests = [
+  {
+    name: 'Empty keySystem string',
+    keySystem: '',
+    options: [ ],
+    shouldPass: false,
+  },
+  {
+    name: 'Empty options specified',
+    keySystem: CLEARKEY_ID,
+    options: [ ],
+    shouldPass: false,
+  },
+  {
+    name: 'Undefined options',
+    keySystem: CLEARKEY_ID,
+    shouldPass: true,
+  },
+  {
+    name: 'Basic MP4 cenc',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'Invalid keysystem failure',
+    keySystem: 'bogusKeySystem',
+    options: [],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid initDataType',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid videoType',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid statefulness',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+        stateful: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid uniqueidentifier',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+        uniqueidentifier: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Audio capabilities not supported by CLEARKEY_ID',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+        audioCapability: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Video capabilities not supported by CLEARKEY_ID',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+        videoCapability: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid option followed by valid',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        bogus: 'bogon',
+      },
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 audio container',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'audio/mp4',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 audio container with AAC-LC',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'audio/mp4; codecs="mp4a.40.2"',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 audio container with invalid codecs',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'audio/mp4; codecs="bogus"',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'MP4 audio container with mp3 is unsupported',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'audio/mp4; codecs="mp3"',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'MP4 video container with mp3 and h264 is unsupported',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4; codecs="avc1.42E01E,mp3"',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'MP4 video container with constrained baseline h.264',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'video/mp4; codecs="avc1.42E01E"',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 video container with invalid codecs',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4; codecs="bogus"',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'MP4 video container with constrained baseline h.264 and AAC-LC',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4; codecs="avc1.42E01E,mp4a.40.2"',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 audio and video type both specified',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4; codecs="avc1.42E01E"',
+        audioType: 'audio/mp4; codecs="mp4a.40.2"',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'WebM CLEARKEY_ID not supported',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'webm',
+        videoType: 'video/webm',
+      }
+    ],
+    shouldPass: false,
+  },
+];
+
+function beginTest() {
+  Promise.all(tests.map(Test)).then(function() { SimpleTest.finish(); });
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.mp4.enabled", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -657,16 +657,18 @@ var interfaceNamesInGlobalScope =
     {name: "MediaKeyError", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaEncryptedEvent", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaKeys", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaKeySession", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MediaKeySystemAccess", pref: "media.eme.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaKeyMessageEvent", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaQueryList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaRecorder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MediaKeySystemAccess.webidl
@@ -0,0 +1,34 @@
+/* -*- 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/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
+ *
+ * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
+ * W3C liability, trademark and document use rules apply.
+ */
+
+enum MediaKeysRequirement {
+  "required",
+  "optional",
+  "disallowed"
+};
+
+dictionary MediaKeySystemOptions {
+  DOMString            initDataType = "";
+  DOMString            audioType = "";
+  DOMString            audioCapability = "";
+  DOMString            videoType = "";
+  DOMString            videoCapability = "";
+  MediaKeysRequirement uniqueidentifier = "optional";
+  MediaKeysRequirement stateful = "optional";
+};
+
+[Pref="media.eme.enabled"]
+interface MediaKeySystemAccess {
+  readonly    attribute DOMString keySystem;
+  [NewObject, Throws]
+  Promise<MediaKeys> createMediaKeys();
+};
--- a/dom/webidl/MediaKeys.webidl
+++ b/dom/webidl/MediaKeys.webidl
@@ -17,14 +17,9 @@ enum SessionType { "temporary", "persist
 interface MediaKeys {
   readonly attribute DOMString keySystem;
 
   [NewObject, Throws]
   MediaKeySession createSession(optional SessionType sessionType = "temporary");
 
   [NewObject, Throws]
   Promise<void> setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate);
-
-  [Throws,NewObject]
-  static Promise<MediaKeys> create(DOMString keySystem);
-  static IsTypeSupportedResult isTypeSupported(DOMString keySystem, optional DOMString initDataType, optional DOMString contentType, optional DOMString capability);
-
 };
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -392,8 +392,17 @@ partial interface Navigator {
   boolean sendBeacon(DOMString url,
                      optional (ArrayBufferView or Blob or DOMString or FormData)? data = null);
 };
 
 partial interface Navigator {
   [Pref="dom.tv.enabled", CheckPermissions="tv", Func="Navigator::HasTVSupport"]
   readonly attribute TVManager? tv;
 };
+
+#ifdef MOZ_EME
+partial interface Navigator {
+  [Pref="media.eme.enabled", Throws, NewObject]
+  Promise<MediaKeySystemAccess>
+  requestMediaKeySystemAccess(DOMString keySystem,
+                              optional sequence<MediaKeySystemOptions> supportedConfigurations);
+};
+#endif
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -793,9 +793,10 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser'
 
 if CONFIG['MOZ_EME']:
     WEBIDL_FILES += [
         'MediaEncryptedEvent.webidl',
         'MediaKeyError.webidl',
         'MediaKeyMessageEvent.webidl',
         'MediaKeys.webidl',
         'MediaKeySession.webidl',
+        'MediaKeySystemAccess.webidl',
     ]