0

Extract supported codecs from CDM component manifest.

Look for "x-cdm-codecs" in the manifest and set its value in
WebPluginInfo::WebPluginMimeType::additional_param_* where it can be used to
determine the types supported.

The manifest is read when registering existing components in addition to new
installs so that the manifest information is always available.

Also updated the non-component path so the behavior is consistent for the
consumer, which will be updated in a separate CL.

BUG=311370
TEST=canPlayType() with various manifest configurations

Review URL: https://codereview.chromium.org/42003002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@231197 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
ddorwin@chromium.org
2013-10-26 10:52:06 +00:00
parent 65668059ff
commit 871bdf15d3
7 changed files with 143 additions and 28 deletions

@ -9,9 +9,11 @@
#include "base/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/logging.h"
#include "base/memory/scoped_handle.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/component_updater/component_patcher.h"
#include "chrome/browser/component_updater/component_updater_service.h"
#include "chrome/common/extensions/extension_constants.h"
@ -85,27 +87,28 @@ class CRXValidator {
std::vector<uint8> public_key_;
};
// Deserialize the CRX manifest. The top level must be a dictionary.
} // namespace.
// TODO(cpu): add a specific attribute check to a component json that the
// extension unpacker will reject, so that a component cannot be installed
// as an extension.
base::DictionaryValue* ReadManifest(const base::FilePath& unpack_path) {
scoped_ptr<base::DictionaryValue> ReadManifest(
const base::FilePath& unpack_path) {
base::FilePath manifest =
unpack_path.Append(FILE_PATH_LITERAL("manifest.json"));
if (!base::PathExists(manifest))
return NULL;
return scoped_ptr<base::DictionaryValue>();
JSONFileValueSerializer serializer(manifest);
std::string error;
scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
if (!root.get())
return NULL;
return scoped_ptr<base::DictionaryValue>();
if (!root->IsType(base::Value::TYPE_DICTIONARY))
return NULL;
return static_cast<base::DictionaryValue*>(root.release());
return scoped_ptr<base::DictionaryValue>();
return scoped_ptr<base::DictionaryValue>(
static_cast<base::DictionaryValue*>(root.release())).Pass();
}
} // namespace.
ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash,
const base::FilePath& path,
const std::string& fingerprint,

@ -9,10 +9,16 @@
#include <vector>
#include "base/basictypes.h"
#include "base/files/file_path.h"
#include "base/json/json_file_value_serializer.h"
#include "base/memory/scoped_ptr.h"
class ComponentInstaller;
class ComponentPatcher;
// Deserializes the CRX manifest. The top level must be a dictionary.
scoped_ptr<base::DictionaryValue> ReadManifest(
const base::FilePath& unpack_path);
// In charge of unpacking the component CRX package and verifying that it is
// well formed and the cryptographic signature is correct. If there is no
// error the component specific installer will be invoked to proceed with

@ -8,6 +8,8 @@
#include "base/files/file_path.h"
#include "base/values.h"
#include "base/version.h"
// TODO(ddorwin): Find a better place for ReadManifest.
#include "chrome/browser/component_updater/component_unpacker.h"
#include "chrome/browser/component_updater/default_component_installer.h"
#include "content/public/browser/browser_thread.h"
@ -17,6 +19,9 @@ namespace {
const char kNullVersion[] = "0.0.0.0";
} // namespace
ComponentInstallerTraits::~ComponentInstallerTraits() {
}
DefaultComponentInstaller::DefaultComponentInstaller(
scoped_ptr<ComponentInstallerTraits> installer_traits)
: current_version_(kNullVersion) {
@ -76,7 +81,13 @@ bool DefaultComponentInstaller::Install(const base::DictionaryValue& manifest,
return false;
}
current_version_ = version;
installer_traits_->ComponentReady(current_version_, GetInstallDirectory());
// TODO(ddorwin): Change the parameter to scoped_ptr<base::DictionaryValue>
// so we can avoid this DeepCopy.
current_manifest_.reset(manifest.DeepCopy());
installer_traits_->ComponentReady(
current_version_,
GetInstallDirectory(),
scoped_ptr<base::DictionaryValue>(current_manifest_->DeepCopy()).Pass());
return true;
}
@ -133,8 +144,17 @@ void DefaultComponentInstaller::StartRegistration(
if (found) {
current_version_ = latest_version;
// TODO(ddorwin): Remove these members and pass them directly to
// FinishRegistration().
base::ReadFileToString(latest_dir.AppendASCII("manifest.fingerprint"),
&current_fingerprint_);
current_manifest_= ReadManifest(latest_dir);
if (!current_manifest_) {
DLOG(ERROR) << "Failed to read manifest for "
<< installer_traits_->GetName() << " ("
<< base_dir.MaybeAsASCII() << ").";
return;
}
}
// Remove older versions of the component. None should be in use during
@ -176,11 +196,16 @@ void DefaultComponentInstaller::FinishRegistration(
}
if (current_version_.CompareTo(base::Version(kNullVersion)) > 0) {
// TODO(ddorwin): Call this function directly the UI thread. The only
// implementation posts back to the UI thread. Then post to UI in Install().
scoped_ptr<base::DictionaryValue> manifest_copy(
current_manifest_->DeepCopy());
content::BrowserThread::PostTask(
content::BrowserThread::FILE, FROM_HERE,
base::Bind(&ComponentInstallerTraits::ComponentReady,
base::Unretained(installer_traits_.get()),
current_version_,
GetInstallDirectory()));
GetInstallDirectory(),
base::Passed(&manifest_copy)));
}
}

@ -24,7 +24,7 @@ class FilePath;
// class.
class ComponentInstallerTraits {
public:
virtual ~ComponentInstallerTraits() {}
virtual ~ComponentInstallerTraits();
// Verifies that a working installation resides within the directory specified
// by |dir|. |dir| is of the form <base directory>/<version>.
@ -49,10 +49,13 @@ class ComponentInstallerTraits {
// notified of a successful install, and is meant to support follow-on work
// such as updating paths elsewhere in Chrome. Called only from the FILE
// thread.
// |version| is the version of the component, while |path| is the path to the
// install directory.
virtual void ComponentReady(const base::Version& version,
const base::FilePath& install_dir) = 0;
// |version| is the version of the component.
// |install_dir| is the path to the install directory for this version.
// |manifest| is the manifest for this version of the component.
virtual void ComponentReady(
const base::Version& version,
const base::FilePath& install_dir,
scoped_ptr<base::DictionaryValue> manifest) = 0;
// Returns the directory that the installer will place versioned installs of
// the component into.
@ -95,6 +98,7 @@ class DefaultComponentInstaller : public ComponentInstaller {
base::Version current_version_;
std::string current_fingerprint_;
scoped_ptr<base::DictionaryValue> current_manifest_;
scoped_ptr<ComponentInstallerTraits> installer_traits_;
DISALLOW_COPY_AND_ASSIGN(DefaultComponentInstaller);

@ -13,7 +13,10 @@
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/component_updater/component_updater_service.h"
@ -75,9 +78,12 @@ base::FilePath GetPlatformDirectory(const base::FilePath& base_path) {
return base_path.AppendASCII("_platform_specific").AppendASCII(platform_arch);
}
bool MakeWidevineCdmPluginInfo(const base::FilePath& path,
const base::Version& version,
content::PepperPluginInfo* plugin_info) {
bool MakeWidevineCdmPluginInfo(
const base::Version& version,
const base::FilePath& path,
const std::vector<base::string16>& additional_param_names,
const std::vector<base::string16>& additional_param_values,
content::PepperPluginInfo* plugin_info) {
if (!version.IsValid() ||
version.components().size() !=
static_cast<size_t>(kWidevineCdmVersionNumComponents)) {
@ -95,19 +101,52 @@ bool MakeWidevineCdmPluginInfo(const base::FilePath& path,
kWidevineCdmPluginMimeType,
kWidevineCdmPluginExtension,
kWidevineCdmPluginMimeTypeDescription);
widevine_cdm_mime_type.additional_param_names = additional_param_names;
widevine_cdm_mime_type.additional_param_values = additional_param_values;
plugin_info->mime_types.push_back(widevine_cdm_mime_type);
plugin_info->permissions = kWidevineCdmPluginPermissions;
return true;
}
void RegisterWidevineCdmWithChrome(const base::FilePath& path,
const base::Version& version) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
content::PepperPluginInfo plugin_info;
if (!MakeWidevineCdmPluginInfo(path, version, &plugin_info))
return;
void GetAdditionalParams(const base::DictionaryValue& manifest,
std::vector<base::string16>* additional_param_names,
std::vector<base::string16>* additional_param_values) {
base::string16 codecs;
if (manifest.GetString("x-cdm-codecs", &codecs)) {
DLOG_IF(WARNING, codecs.empty())
<< "Widevine CDM component manifest has empty codecs list";
additional_param_names->push_back(
base::ASCIIToUTF16(kCdmSupportedCodecsParamName));
additional_param_values->push_back(codecs);
} else {
DLOG(WARNING) << "Widevine CDM component manifest is missing codecs";
// TODO(ddorwin): Remove this once all users have been updated.
// The original manifests did not include this string, so add the base set.
additional_param_names->push_back(
base::ASCIIToUTF16(kCdmSupportedCodecsParamName));
additional_param_values->push_back(base::ASCIIToUTF16("vp8,vorbis"));
}
}
void RegisterWidevineCdmWithChrome(const base::Version& version,
const base::FilePath& path,
scoped_ptr<base::DictionaryValue> manifest) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::vector<base::string16> additional_param_names;
std::vector<base::string16> additional_param_values;
GetAdditionalParams(
*manifest, &additional_param_names, &additional_param_values);
content::PepperPluginInfo plugin_info;
if (!MakeWidevineCdmPluginInfo(version,
path,
additional_param_names,
additional_param_values,
&plugin_info)) {
return;
}
// true = Add to beginning of list to override any existing registrations.
PluginService::GetInstance()->RegisterInternalPlugin(
plugin_info.ToWebPluginInfo(), true);
PluginService::GetInstance()->RefreshPlugins();
@ -127,8 +166,10 @@ class WidevineCdmComponentInstallerTraits : public ComponentInstallerTraits {
const base::FilePath& install_dir) OVERRIDE;
virtual bool VerifyInstallation(
const base::FilePath& install_dir) const OVERRIDE;
virtual void ComponentReady(const base::Version& version,
const base::FilePath& path) OVERRIDE;
virtual void ComponentReady(
const base::Version& version,
const base::FilePath& path,
scoped_ptr<base::DictionaryValue> manifest) OVERRIDE;
virtual base::FilePath GetBaseDirectory() const OVERRIDE;
virtual void GetHash(std::vector<uint8>* hash) const OVERRIDE;
virtual std::string GetName() const OVERRIDE;
@ -156,11 +197,14 @@ bool WidevineCdmComponentInstallerTraits::OnCustomInstall(
// Once the component is installed, register the new version with Chrome.
void WidevineCdmComponentInstallerTraits::ComponentReady(
const base::Version& version,
const base::FilePath& path) {
const base::FilePath& path,
scoped_ptr<base::DictionaryValue> manifest) {
// TODO(ddorwin): Check API version compatibility. Return if fails.
base::FilePath adapter_install_path = GetPlatformDirectory(path)
.AppendASCII(kWidevineCdmAdapterFileName);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
&RegisterWidevineCdmWithChrome, adapter_install_path, version));
&RegisterWidevineCdmWithChrome,
version, adapter_install_path, base::Passed(&manifest)));
}
bool WidevineCdmComponentInstallerTraits::VerifyInstallation(

@ -9,6 +9,7 @@
#include "base/debug/crash_logging.h"
#include "base/file_util.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/string_util.h"
@ -289,6 +290,28 @@ void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) {
kWidevineCdmPluginMimeType,
kWidevineCdmPluginExtension,
kWidevineCdmPluginMimeTypeDescription);
// Add the supported codecs as if they came from the component manifest.
std::vector<std::string> codecs;
codecs.push_back(kCdmSupportedCodecVorbis);
codecs.push_back(kCdmSupportedCodecVp8);
#if defined(USE_PROPRIETARY_CODECS)
// TODO(ddorwin): Rename these macros to reflect their real meaning: whether the
// CDM Chrome was built [and shipped] with support these types.
#if defined(WIDEVINE_CDM_AAC_SUPPORT_AVAILABLE)
codecs.push_back(kCdmSupportedCodecAac);
#endif
#if defined(WIDEVINE_CDM_AVC1_SUPPORT_AVAILABLE)
codecs.push_back(kCdmSupportedCodecAvc1);
#endif
#endif // defined(USE_PROPRIETARY_CODECS)
std::string codec_string =
JoinString(codecs, kCdmSupportedCodecsValueDelimiter);
widevine_cdm_mime_type.additional_param_names.push_back(
base::ASCIIToUTF16(kCdmSupportedCodecsParamName));
widevine_cdm_mime_type.additional_param_values.push_back(
base::ASCIIToUTF16(codec_string));
widevine_cdm.mime_types.push_back(widevine_cdm_mime_type);
widevine_cdm.permissions = kWidevineCdmPluginPermissions;
plugins->push_back(widevine_cdm);

@ -45,6 +45,16 @@ const char kWidevineCdmAdapterFileName[] =
"libwidevinecdmadapter.so";
#endif
// The following strings are used to communicate supported codecs (from the
// component manifest) via WebPluginInfo::WebPluginMimeType's additional params.
const char kCdmSupportedCodecsParamName[] = "codecs";
const char kCdmSupportedCodecsValueDelimiter = ',';
const char kCdmSupportedCodecVorbis[] = "vorbis";
const char kCdmSupportedCodecVp8[] = "vp8";
#if defined(USE_PROPRIETARY_CODECS)
const char kCdmSupportedCodecAac[] = "aac";
const char kCdmSupportedCodecAvc1[] = "avc1";
#endif // defined(USE_PROPRIETARY_CODECS)
#if defined(OS_MACOSX) || defined(OS_WIN)
// CDM is installed by the component installer instead of the Chrome installer.