blob: ea101929351aa33e44f4577bc4594d9464b169c5 [file] [log] [blame]
// Copyright 2014 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.
#include "chromecast/common/cast_content_client.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/native_library.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "build/build_config.h"
#include "chromecast/base/cast_constants.h"
#include "chromecast/base/cast_paths.h"
#include "chromecast/base/version.h"
#include "chromecast/chromecast_buildflags.h"
#include "components/cast/common/constants.h"
#include "content/public/common/cdm_info.h"
#include "content/public/common/user_agent.h"
#include "media/base/media_switches.h"
#include "media/media_buildflags.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "third_party/widevine/cdm/buildflags.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "url/url_util.h"
#if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS)
#include "extensions/common/constants.h" // nogncheck
#endif
#if defined(OS_ANDROID)
#include "chromecast/common/media/cast_media_drm_bridge_client.h"
#endif
#if !defined(OS_FUCHSIA)
#include "base/no_destructor.h"
#include "components/services/heap_profiling/public/cpp/profiling_client.h" // nogncheck
#include "mojo/public/cpp/bindings/pending_receiver.h"
#endif
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
#include "media/cdm/cdm_paths.h" // nogncheck
#endif
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
#include "base/no_destructor.h"
#include "components/cdm/common/cdm_manifest.h"
#include "media/cdm/cdm_capability.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h" // nogncheck
// component updated CDM on all desktop platforms and remove this.
// This file is In SHARED_INTERMEDIATE_DIR.
#include "widevine_cdm_version.h" // nogncheck
#endif
namespace chromecast {
namespace shell {
namespace {
#if defined(OS_ANDROID)
std::string BuildAndroidOsInfo() {
int32_t os_major_version = 0;
int32_t os_minor_version = 0;
int32_t os_bugfix_version = 0;
base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
&os_minor_version,
&os_bugfix_version);
std::string android_version_str;
base::StringAppendF(
&android_version_str, "%d.%d", os_major_version, os_minor_version);
if (os_bugfix_version != 0)
base::StringAppendF(&android_version_str, ".%d", os_bugfix_version);
std::string android_info_str;
// Append the build ID.
std::string android_build_id = base::SysInfo::GetAndroidBuildID();
if (android_build_id.size() > 0)
android_info_str += "; Build/" + android_build_id;
std::string os_info;
base::StringAppendF(
&os_info,
"Android %s%s",
android_version_str.c_str(),
android_info_str.c_str());
return os_info;
}
#endif
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
// Copied from chrome_content_client.cc
std::unique_ptr<content::CdmInfo> CreateWidevineCdmInfo(
const base::Version& version,
const base::FilePath& cdm_library_path,
media::CdmCapability capability) {
return std::make_unique<content::CdmInfo>(
kWidevineKeySystem, content::CdmInfo::Robustness::kSoftwareSecure,
std::move(capability), /*supports_sub_key_systems=*/false,
kWidevineCdmDisplayName, kWidevineCdmGuid, version, cdm_library_path,
kWidevineCdmFileSystemId);
}
// On desktop Linux, given |cdm_base_path| that points to a folder containing
// the Widevine CDM and associated files, read the manifest included in that
// directory and create a CdmInfo. If that is successful, return the CdmInfo. If
// not, return nullptr.
// Copied from chrome_content_client.cc
// TODO(crbug.com/1174571): move the functions to a common file.
std::unique_ptr<content::CdmInfo> CreateCdmInfoFromWidevineDirectory(
const base::FilePath& cdm_base_path) {
// Library should be inside a platform specific directory.
auto cdm_library_path =
media::GetPlatformSpecificDirectory(cdm_base_path)
.Append(base::GetNativeLibraryName(kWidevineCdmLibraryName));
if (!base::PathExists(cdm_library_path)) {
LOG(ERROR) << "cdm library path doesn't exist";
return nullptr;
}
// Manifest should be at the top level.
auto manifest_path = cdm_base_path.Append(FILE_PATH_LITERAL("manifest.json"));
base::Version version;
media::CdmCapability capability;
if (!ParseCdmManifestFromPath(manifest_path, &version, &capability))
return nullptr;
return CreateWidevineCdmInfo(version, cdm_library_path,
std::move(capability));
}
// This code checks to see if the Widevine CDM was bundled with Chrome. If one
// can be found and looks valid, it returns the CdmInfo for the CDM. Otherwise
// it returns nullptr.
// Copied from chrome_content_client.cc
content::CdmInfo* GetBundledWidevine() {
// We only want to do this on the first call, as if Widevine wasn't bundled
// with Chrome (or it was deleted/removed) it won't be loaded into the zygote.
static base::NoDestructor<std::unique_ptr<content::CdmInfo>> s_cdm_info(
[]() -> std::unique_ptr<content::CdmInfo> {
base::FilePath install_dir;
CHECK(base::PathService::Get(chromecast::DIR_BUNDLED_WIDEVINE_CDM,
&install_dir));
// On desktop Linux the MANIFEST is bundled with the CDM.
return CreateCdmInfoFromWidevineDirectory(install_dir);
}());
return s_cdm_info->get();
}
#endif // BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
} // namespace
std::string GetUserAgent() {
std::string product = "Chrome/" PRODUCT_VERSION;
std::string os_info;
base::StringAppendF(
&os_info,
"%s%s",
#if defined(OS_ANDROID)
"Linux; ",
BuildAndroidOsInfo().c_str()
#elif BUILDFLAG(USE_ANDROID_USER_AGENT)
"Linux; ", "Android"
#else
"X11; ",
content::BuildOSCpuInfo(content::IncludeAndroidBuildNumber::Exclude,
content::IncludeAndroidModel::Include)
.c_str()
#endif
);
return content::BuildUserAgentFromOSAndProduct(os_info, product) + " CrKey/" +
kFrozenCrKeyValue;
}
CastContentClient::~CastContentClient() {
}
void CastContentClient::SetActiveURL(const GURL& url, std::string top_origin) {
if (url.is_empty() || url == last_active_url_)
return;
LOG(INFO) << "Active URL: " << url.possibly_invalid_spec() << " for origin '"
<< top_origin << "'";
last_active_url_ = url;
}
void CastContentClient::AddAdditionalSchemes(Schemes* schemes) {
schemes->standard_schemes.push_back(kChromeResourceScheme);
#if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS)
schemes->standard_schemes.push_back(extensions::kExtensionScheme);
// Treat as secure because we only load extension code written by us.
schemes->secure_schemes.push_back(extensions::kExtensionScheme);
schemes->service_worker_schemes.push_back(extensions::kExtensionScheme);
schemes->csp_bypassing_schemes.push_back(extensions::kExtensionScheme);
#endif
}
std::u16string CastContentClient::GetLocalizedString(int message_id) {
return l10n_util::GetStringUTF16(message_id);
}
base::StringPiece CastContentClient::GetDataResource(
int resource_id,
ui::ScaleFactor scale_factor) {
return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
resource_id, scale_factor);
}
base::RefCountedMemory* CastContentClient::GetDataResourceBytes(
int resource_id) {
// Chromecast loads localized resources for the home screen via this code
// path. See crbug.com/643886 for details.
return ui::ResourceBundle::GetSharedInstance().LoadLocalizedResourceBytes(
resource_id);
}
gfx::Image& CastContentClient::GetNativeImageNamed(int resource_id) {
return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
resource_id);
}
#if defined(OS_ANDROID)
::media::MediaDrmBridgeClient* CastContentClient::GetMediaDrmBridgeClient() {
return new media::CastMediaDrmBridgeClient();
}
#endif // OS_ANDROID
void CastContentClient::ExposeInterfacesToBrowser(
scoped_refptr<base::SequencedTaskRunner> io_task_runner,
mojo::BinderMap* binders) {
#if !defined(OS_FUCHSIA)
binders->Add(
base::BindRepeating(
[](mojo::PendingReceiver<heap_profiling::mojom::ProfilingClient>
receiver) {
static base::NoDestructor<heap_profiling::ProfilingClient>
profiling_client;
profiling_client->BindToInterface(std::move(receiver));
}),
io_task_runner);
#endif // !defined(OS_FUCHSIA)
}
void CastContentClient::AddContentDecryptionModules(
std::vector<content::CdmInfo>* cdms,
std::vector<::media::CdmHostFilePath>* cdm_host_file_paths) {
if (cdms) {
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
// The Widevine CDM on Linux needs to be registered (and loaded) before the
// zygote is locked down. The CDM can be found from the version bundled with
// Chrome (if BUNDLE_WIDEVINE_CDM = true).
content::CdmInfo* bundled_widevine = GetBundledWidevine();
if (bundled_widevine) {
DVLOG(1) << "Registering bundled Widevine " << bundled_widevine->version;
cdms->push_back(*bundled_widevine);
} else {
DVLOG(1) << "Widevine enabled but no library found";
}
#endif // BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
}
}
} // namespace shell
} // namespace chromecast