diff --git a/chrome/browser/component_updater/DEPS b/chrome/browser/component_updater/DEPS index 2bac0cf70f9952..6858ef79d5fc18 100644 --- a/chrome/browser/component_updater/DEPS +++ b/chrome/browser/component_updater/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+media/cdm/ppapi/supported_cdm_versions.h", "+ppapi/shared_impl/ppapi_permissions.h", "+ppapi/thunk", "+third_party/widevine" diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc index e65fec78c078f9..87977ffd629b0f 100644 --- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc +++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc @@ -16,6 +16,8 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "build/build_config.h" @@ -28,6 +30,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/plugin_service.h" #include "content/public/common/pepper_plugin_info.h" +#include "media/cdm/ppapi/supported_cdm_versions.h" #include "third_party/widevine/cdm/widevine_cdm_common.h" #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. @@ -68,6 +71,23 @@ const char kWidevineCdmArch[] = "???"; #endif +// The CDM manifest includes several custom values, all beginning with "x-cdm-". +// All values are strings. +// All values that are lists are delimited by commas. No trailing commas. +// For example, "1,2,4". +const char kCdmValueDelimiter = ','; +COMPILE_ASSERT(kCdmValueDelimiter == kCdmSupportedCodecsValueDelimiter, + cdm_delimiters_do_not_match); +// The following entries are required. +// Interface versions are lists of integers (e.g. "1" or "1,2,4"). +// These are checked in this file before registering the CDM. +const char kCdmModuleVersionsName[] = "x-cdm-module-versions"; +const char kCdmInstanceVersionsName[] = "x-cdm-instance-versions"; +const char kCdmHostVersionsName[] = "x-cdm-host-versions"; +// The codecs list is a list of simple codec names (e.g. "vp8,vorbis"). +// The list is passed to other parts of Chrome. +const char kCdmCodecsListName[] = "x-cdm-codecs"; + // Widevine CDM is packaged as a multi-CRX. Widevine CDM binaries are located in // _platform_specific/ folder in the package. This function // returns the platform-specific subdirectory that is part of that multi-CRX. @@ -109,11 +129,67 @@ bool MakeWidevineCdmPluginInfo( return true; } +typedef bool (*VersionCheckFunc)(int version); + +bool CheckForCompatibleVersion(const base::DictionaryValue& manifest, + const std::string version_name, + VersionCheckFunc version_check_func) { + std::string versions_string; + if (!manifest.GetString(version_name, &versions_string)) { + DLOG(WARNING) + << "Widevine CDM component manifest is missing " << version_name; + // TODO(ddorwin): Remove this once all users have been updated. + // The original manifests did not include this string, so add its version. + if (version_name == kCdmModuleVersionsName) + versions_string = "4"; + else if (version_name == kCdmInstanceVersionsName) + versions_string = "1"; + else if (version_name == kCdmHostVersionsName) + versions_string = "1"; + } + DLOG_IF(WARNING, versions_string.empty()) + << "Widevine CDM component manifest has empty " << version_name; + + std::vector versions; + base::SplitString(versions_string, + kCdmValueDelimiter, + &versions); + + for (size_t i = 0; i < versions.size(); ++i) { + int version = 0; + if (base::StringToInt(versions[i], &version)) + if (version_check_func(version)) + return true; + } + + DLOG(WARNING) << "Widevine CDM component manifest has no supported " + << version_name << " in '" << versions_string << "'"; + return false; +} + +// Returns whether the CDM's API versions, as specified in the manifest, are +// compatible with this Chrome binary. +// Checks the module API, CDM instance API, and Host API. +// This should never fail except in rare cases where the component has not been +// updated recently or the user downgrades Chrome. +bool IsCompatibleWithChrome(const base::DictionaryValue& manifest) { + return + CheckForCompatibleVersion(manifest, + kCdmModuleVersionsName, + media::IsSupportedCdmModuleVersion) && + CheckForCompatibleVersion(manifest, + kCdmInstanceVersionsName, + media::IsSupportedCdmInterfaceVersion) && + CheckForCompatibleVersion(manifest, + kCdmHostVersionsName, + media::IsSupportedCdmHostVersion); +} + void GetAdditionalParams(const base::DictionaryValue& manifest, std::vector* additional_param_names, std::vector* additional_param_values) { base::string16 codecs; - if (manifest.GetString("x-cdm-codecs", &codecs)) { + if (manifest.GetString(kCdmCodecsListName, &codecs)) { DLOG_IF(WARNING, codecs.empty()) << "Widevine CDM component manifest has empty codecs list"; additional_param_names->push_back( @@ -199,7 +275,11 @@ void WidevineCdmComponentInstallerTraits::ComponentReady( const base::Version& version, const base::FilePath& path, scoped_ptr manifest) { - // TODO(ddorwin): Check API version compatibility. Return if fails. + if (!IsCompatibleWithChrome(*manifest)) { + DLOG(WARNING) << "Installed Widevine CDM component is incompatible."; + return; + } + base::FilePath adapter_install_path = GetPlatformDirectory(path) .AppendASCII(kWidevineCdmAdapterFileName); RegisterWidevineCdmWithChrome(version, diff --git a/media/cdm/ppapi/cdm_adapter.cc b/media/cdm/ppapi/cdm_adapter.cc index 006ce096ed0575..f8adb03ba5199a 100644 --- a/media/cdm/ppapi/cdm_adapter.cc +++ b/media/cdm/ppapi/cdm_adapter.cc @@ -5,6 +5,7 @@ #include "media/cdm/ppapi/cdm_adapter.h" #include "media/cdm/ppapi/cdm_helpers.h" +#include "media/cdm/ppapi/supported_cdm_versions.h" #if defined(CHECK_DOCUMENT_URL) #include "ppapi/cpp/dev/url_util_dev.h" @@ -912,12 +913,34 @@ void* GetCdmHost(int host_interface_version, void* user_data) { if (!host_interface_version || !user_data) return NULL; + COMPILE_ASSERT(cdm::ContentDecryptionModule::Host::kVersion == + cdm::ContentDecryptionModule_2::Host::kVersion, + update_code_below); + + // Ensure IsSupportedCdmHostVersion matches implementation of this function. + // Always update this DCHECK when updating this function. + // If this check fails, update this function and DCHECK or update + // IsSupportedCdmHostVersion. + PP_DCHECK( + // Future version is not supported. + !IsSupportedCdmHostVersion( + cdm::ContentDecryptionModule::Host::kVersion + 1) && + // Current version is supported. + IsSupportedCdmHostVersion(cdm::ContentDecryptionModule::Host::kVersion) && + // Include all previous supported versions here. + IsSupportedCdmHostVersion(cdm::Host_1::kVersion) && + // One older than the oldest supported version is not supported. + !IsSupportedCdmHostVersion(cdm::Host_1::kVersion - 1)); + PP_DCHECK(IsSupportedCdmHostVersion(host_interface_version)); + CdmAdapter* cdm_adapter = static_cast(user_data); switch (host_interface_version) { - case cdm::ContentDecryptionModule_1::Host::kVersion: - return static_cast(cdm_adapter); - case cdm::ContentDecryptionModule_2::Host::kVersion: - return static_cast(cdm_adapter); + // The latest CDM host version. + case cdm::ContentDecryptionModule::Host::kVersion: + return static_cast(cdm_adapter); + // Older supported version(s) of the CDM host. + case cdm::Host_1::kVersion: + return static_cast(cdm_adapter); default: PP_NOTREACHED(); return NULL; diff --git a/media/cdm/ppapi/cdm_wrapper.h b/media/cdm/ppapi/cdm_wrapper.h index f54d0d650b684d..166d89cf6cf98b 100644 --- a/media/cdm/ppapi/cdm_wrapper.h +++ b/media/cdm/ppapi/cdm_wrapper.h @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "media/cdm/ppapi/api/content_decryption_module.h" #include "media/cdm/ppapi/cdm_helpers.h" +#include "media/cdm/ppapi/supported_cdm_versions.h" #include "ppapi/cpp/logging.h" namespace media { @@ -215,22 +216,40 @@ CdmWrapper* CdmWrapper::Create(const char* key_system, uint32_t key_system_size, GetCdmHostFunc get_cdm_host_func, void* user_data) { + COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == + cdm::ContentDecryptionModule_2::kVersion, + update_code_below); + + // Ensure IsSupportedCdmInterfaceVersion matches this implementation. + // Always update this DCHECK when updating this function. + // If this check fails, update this function and DCHECK or update + // IsSupportedCdmInterfaceVersion. + PP_DCHECK( + !IsSupportedCdmInterfaceVersion( + cdm::ContentDecryptionModule::kVersion + 1) && + IsSupportedCdmInterfaceVersion(cdm::ContentDecryptionModule::kVersion) && + IsSupportedCdmInterfaceVersion( + cdm::ContentDecryptionModule_1::kVersion) && + !IsSupportedCdmInterfaceVersion( + cdm::ContentDecryptionModule_1::kVersion - 1)); + // Try to create the CDM using the latest CDM interface version. - CdmWrapper* cdm_adapter = + CdmWrapper* cdm_wrapper = CdmWrapperImpl::Create( key_system, key_system_size, get_cdm_host_func, user_data); - if (cdm_adapter) - return cdm_adapter; + if (cdm_wrapper) + return cdm_wrapper; - // Try to see if the CDM supports older version(s) of CDM interface(s). - cdm_adapter = CdmWrapperImpl::Create( + // Try to see if the CDM supports older version(s) of the CDM interface. + cdm_wrapper = CdmWrapperImpl::Create( key_system, key_system_size, get_cdm_host_func, user_data); - return cdm_adapter; + return cdm_wrapper; } // When updating the CdmAdapter, ensure you've updated the CdmWrapper to contain // stub implementations for new or modified methods that the older CDM interface // does not have. +// Also update supported_cdm_versions.h. COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == cdm::ContentDecryptionModule_2::kVersion, ensure_cdm_wrapper_templates_have_old_version_support); diff --git a/media/cdm/ppapi/supported_cdm_versions.h b/media/cdm/ppapi/supported_cdm_versions.h new file mode 100644 index 00000000000000..b509e5082afe1a --- /dev/null +++ b/media/cdm/ppapi/supported_cdm_versions.h @@ -0,0 +1,56 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_CDM_PPAPI_SUPPORTED_CDM_VERSIONS_H_ +#define MEDIA_CDM_PPAPI_SUPPORTED_CDM_VERSIONS_H_ + +#include "media/cdm/ppapi/api/content_decryption_module.h" + +namespace media { + +// TODO(ddorwin): Move to content_decryption_module.h. +#define CDM_MODULE_VERSION 4 + +bool IsSupportedCdmModuleVersion(int version) { + switch(version) { + // Latest. + case CDM_MODULE_VERSION: + return true; + default: + return false; + } +} + +bool IsSupportedCdmInterfaceVersion(int version) { + COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == + cdm::ContentDecryptionModule_2::kVersion, + update_code_below); + switch(version) { + // Latest. + case cdm::ContentDecryptionModule::kVersion: + // Older supported versions. + case cdm::ContentDecryptionModule_1::kVersion: + return true; + default: + return false; + } +} + +bool IsSupportedCdmHostVersion(int version) { + COMPILE_ASSERT(cdm::ContentDecryptionModule::Host::kVersion == + cdm::ContentDecryptionModule_2::Host::kVersion, + update_code_below); + switch(version) { + // Supported versions in increasing order (there is no default). + case cdm::Host_1::kVersion: + case cdm::Host_2::kVersion: + return true; + default: + return false; + } +} + +} // namespace media + +#endif // MEDIA_CDM_PPAPI_SUPPORTED_CDM_VERSIONS_H_ diff --git a/media/media_cdm.gypi b/media/media_cdm.gypi index 0a594ddbf73f10..8ebcfbe89ab29c 100644 --- a/media/media_cdm.gypi +++ b/media/media_cdm.gypi @@ -105,6 +105,7 @@ 'cdm/ppapi/cdm_helpers.h', 'cdm/ppapi/cdm_wrapper.h', 'cdm/ppapi/linked_ptr.h', + 'cdm/ppapi/supported_cdm_versions.h', ], 'conditions': [ ['os_posix == 1 and OS != "mac" and enable_pepper_cdms==1', { diff --git a/third_party/widevine/cdm/widevine_cdm.gyp b/third_party/widevine/cdm/widevine_cdm.gyp index 3028bd59d91f58..eaf376a1560d32 100644 --- a/third_party/widevine/cdm/widevine_cdm.gyp +++ b/third_party/widevine/cdm/widevine_cdm.gyp @@ -67,6 +67,7 @@ '<(DEPTH)/media/cdm/ppapi/cdm_helpers.h', '<(DEPTH)/media/cdm/ppapi/cdm_wrapper.h', '<(DEPTH)/media/cdm/ppapi/linked_ptr.h', + '<(DEPTH)/media/cdm/ppapi/supported_cdm_versions.h', ], 'conditions': [ [ 'os_posix == 1 and OS != "mac"', {