/* -*- 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 MediaKeySystemAccess::CreateMediaKeys(ErrorResult& aRv) { nsRefPtr 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 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 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& aOptions) { nsCOMPtr 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