#include "chrome/browser/ui/webui/about/about_ui.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/containers/to_vector.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/about_flags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "components/about_ui/credit_utils.h"
#include "components/grit/components_resources.h"
#include "components/strings/grit/components_locale_settings.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/webui_config_map.h"
#include "content/public/common/url_constants.h"
#include "net/base/filename_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/web_ui_util.h"
#include "url/gurl.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/webui/theme_source.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include <map>
#include "base/base64.h"
#include "base/strings/strcat.h"
#include "chrome/browser/ash/borealis/borealis_credits.h"
#include "chrome/browser/ash/crosapi/browser_manager.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/crostini/crostini_features.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/customization/customization_document.h"
#include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
#include "chrome/browser/ash/login/wizard_controller.h"
#include "chrome/browser/browser_process_platform_part_ash.h"
#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
#include "chrome/common/webui_url_constants.h"
#include "chromeos/ash/components/system/statistics_provider.h"
#include "components/component_updater/ash/component_manager_ash.h"
#include "components/language/core/common/locale_util.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "third_party/zlib/google/compression_utils.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/lacros/lacros_url_handling.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/common/webui_url_constants.h"
#endif
BrowserThread;
namespace {
constexpr char kCreditsJsPath[] = …;
constexpr char kCreditsCssPath[] = …;
constexpr char kStatsJsPath[] = …;
constexpr char kStringsJsPath[] = …;
#if BUILDFLAG(IS_CHROMEOS_ASH)
constexpr char kTerminaCreditsPath[] = "about_os_credits.html";
class ChromeOSTermsHandler
: public base::RefCountedThreadSafe<ChromeOSTermsHandler> {
public:
ChromeOSTermsHandler(const ChromeOSTermsHandler&) = delete;
ChromeOSTermsHandler& operator=(const ChromeOSTermsHandler&) = delete;
static void Start(const std::string& path,
content::URLDataSource::GotDataCallback callback) {
scoped_refptr<ChromeOSTermsHandler> handler(
new ChromeOSTermsHandler(path, std::move(callback)));
handler->StartOnUIThread();
}
private:
friend class base::RefCountedThreadSafe<ChromeOSTermsHandler>;
ChromeOSTermsHandler(const std::string& path,
content::URLDataSource::GotDataCallback callback)
: path_(path),
callback_(std::move(callback)),
locale_(g_browser_process->GetApplicationLocale()) {}
virtual ~ChromeOSTermsHandler() {}
void StartOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (path_ == chrome::kOemEulaURLPath) {
base::ThreadPool::PostTaskAndReply(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&ChromeOSTermsHandler::LoadOemEulaFileAsync, this),
base::BindOnce(&ChromeOSTermsHandler::ResponseOnUIThread, this));
} else if (path_ == chrome::kArcTermsURLPath) {
LOG(WARNING) << "Could not load offline Play Store ToS.";
} else if (path_ == chrome::kArcPrivacyPolicyURLPath) {
LOG(WARNING) << "Could not load offline Play Store privacy policy.";
} else {
NOTREACHED_IN_MIGRATION();
ResponseOnUIThread();
}
}
void LoadOemEulaFileAsync() {
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
const ash::StartupCustomizationDocument* customization =
ash::StartupCustomizationDocument::GetInstance();
if (!customization->IsReady())
return;
base::FilePath oem_eula_file_path;
if (net::FileURLToFilePath(GURL(customization->GetEULAPage(locale_)),
&oem_eula_file_path)) {
if (!base::ReadFileToString(oem_eula_file_path, &contents_)) {
contents_.clear();
}
}
}
void ResponseOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (contents_.empty() && path_.empty()) {
contents_ =
ui::ResourceBundle::GetSharedInstance().LoadLocalizedResourceString(
IDS_TERMS_HTML);
}
std::move(callback_).Run(
base::MakeRefCounted<base::RefCountedString>(std::move(contents_)));
}
const std::string path_;
content::URLDataSource::GotDataCallback callback_;
const std::string locale_;
std::string contents_;
};
class ChromeOSCreditsHandler
: public base::RefCountedThreadSafe<ChromeOSCreditsHandler> {
public:
ChromeOSCreditsHandler(const ChromeOSCreditsHandler&) = delete;
ChromeOSCreditsHandler& operator=(const ChromeOSCreditsHandler&) = delete;
static void Start(const std::string& path,
content::URLDataSource::GotDataCallback callback,
const base::FilePath& prefix) {
scoped_refptr<ChromeOSCreditsHandler> handler(
new ChromeOSCreditsHandler(path, std::move(callback), prefix));
handler->StartOnUIThread();
}
private:
friend class base::RefCountedThreadSafe<ChromeOSCreditsHandler>;
ChromeOSCreditsHandler(const std::string& path,
content::URLDataSource::GotDataCallback callback,
const base::FilePath& prefix)
: path_(path), callback_(std::move(callback)), prefix_(prefix) {}
virtual ~ChromeOSCreditsHandler() {}
void StartOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::ThreadPool::PostTaskAndReply(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&ChromeOSCreditsHandler::LoadCreditsFileAsync, this),
base::BindOnce(&ChromeOSCreditsHandler::ResponseOnUIThread, this));
}
void LoadCreditsFileAsync() {
if (prefix_.empty()) {
prefix_ = base::FilePath(chrome::kChromeOSCreditsPath).DirName();
}
base::FilePath credits =
prefix_.Append(base::FilePath(chrome::kChromeOSCreditsPath).BaseName());
if (base::ReadFileToString(credits, &contents_)) {
return;
}
base::FilePath compressed_credits = prefix_.Append(
base::FilePath(chrome::kChromeOSCreditsCompressedPath).BaseName());
std::string compressed;
if (!base::ReadFileToString(compressed_credits, &compressed)) {
contents_.clear();
return;
}
if (!compression::GzipUncompress(compressed, &contents_)) {
LOG(DFATAL) << "Decompressing os credits failed";
contents_.clear();
return;
}
}
void ResponseOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (contents_.empty()) {
contents_ =
ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
IDR_OS_CREDITS_HTML);
}
std::move(callback_).Run(
base::MakeRefCounted<base::RefCountedString>(std::move(contents_)));
}
const std::string path_;
content::URLDataSource::GotDataCallback callback_;
std::string contents_;
base::FilePath prefix_;
};
void OnBorealisCreditsLoaded(content::URLDataSource::GotDataCallback callback,
std::string credits_html) {
if (credits_html.empty()) {
credits_html = l10n_util::GetStringUTF8(IDS_BOREALIS_CREDITS_PLACEHOLDER);
}
std::move(callback).Run(
base::MakeRefCounted<base::RefCountedString>(std::move(credits_html)));
}
void HandleBorealisCredits(Profile* profile,
content::URLDataSource::GotDataCallback callback) {
borealis::LoadBorealisCredits(
profile, base::BindOnce(&OnBorealisCreditsLoaded, std::move(callback)));
}
class CrostiniCreditsHandler
: public base::RefCountedThreadSafe<CrostiniCreditsHandler> {
public:
CrostiniCreditsHandler(const CrostiniCreditsHandler&) = delete;
CrostiniCreditsHandler& operator=(const CrostiniCreditsHandler&) = delete;
static void Start(Profile* profile,
const std::string& path,
content::URLDataSource::GotDataCallback callback) {
scoped_refptr<CrostiniCreditsHandler> handler(
new CrostiniCreditsHandler(profile, path, std::move(callback)));
handler->StartOnUIThread();
}
private:
friend class base::RefCountedThreadSafe<CrostiniCreditsHandler>;
CrostiniCreditsHandler(Profile* profile,
const std::string& path,
content::URLDataSource::GotDataCallback callback)
: path_(path), callback_(std::move(callback)), profile_(profile) {}
virtual ~CrostiniCreditsHandler() {}
void StartOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (crostini::CrostiniFeatures::Get()->IsAllowedNow(profile_)) {
crostini::CrostiniManager::GetForProfile(profile_)->GetInstallLocation(
base::BindOnce(&CrostiniCreditsHandler::LoadCredits, this));
} else {
RespondWithPlaceholder();
}
}
void LoadCredits(base::FilePath path) {
if (path.empty()) {
RespondWithPlaceholder();
return;
}
base::ThreadPool::PostTaskAndReply(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&CrostiniCreditsHandler::LoadCrostiniCreditsFileAsync,
this, path.Append(kTerminaCreditsPath)),
base::BindOnce(&CrostiniCreditsHandler::RespondOnUIThread, this));
}
void LoadCrostiniCreditsFileAsync(base::FilePath credits_file_path) {
if (!base::ReadFileToString(credits_file_path, &contents_)) {
contents_.clear();
}
}
void RespondWithPlaceholder() {
contents_.clear();
RespondOnUIThread();
}
void RespondOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (contents_.empty()) {
contents_ = l10n_util::GetStringUTF8(IDS_CROSTINI_CREDITS_PLACEHOLDER);
}
std::move(callback_).Run(
base::MakeRefCounted<base::RefCountedString>(std::move(contents_)));
}
const std::string path_;
content::URLDataSource::GotDataCallback callback_;
std::string contents_;
raw_ptr<Profile> profile_;
};
#endif
}
namespace about_ui {
void AppendHeader(std::string* output, const std::string& unescaped_title) { … }
#if BUILDFLAG(IS_CHROMEOS)
bool isLacrosPrimaryOrCurrentBrowser() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
return crosapi::browser_util::IsLacrosEnabled();
#else
return true;
#endif
}
void AppendBody(std::string* output) {
if (isLacrosPrimaryOrCurrentBrowser()) {
output->append(
"<link rel='stylesheet' href='chrome://resources/css/os_header.css'>\n"
"</head>\n<body>\n"
"<div class='os-link-container-container' id='os-link-container'>\n"
"<div class='os-link-container'>\n"
"<span class='os-link-icon'></span>\n"
"<span aria-hidden='true' id='os-link-desc'>" +
l10n_util::GetStringUTF8(IDS_ABOUT_OS_TEXT1_LABEL) +
"</span>\n"
"<a href='#' id='os-link-href' aria-describedby='os-link-desc'>" +
l10n_util::GetStringUTF8(IDS_ABOUT_OS_LINK) +
"</a>\n<span aria-hidden='true'>" +
l10n_util::GetStringUTF8(IDS_ABOUT_OS_TEXT2_LABEL) +
"</span>\n</div>\n</div>\n");
} else {
output->append("</head>\n<body>\n");
}
}
void AppendFooter(std::string* output) {
if (isLacrosPrimaryOrCurrentBrowser()) {
output->append(
"<script type='module' src='chrome://resources/js/os_about.js'>"
"</script>\n");
}
output->append("</body>\n</html>\n");
}
#else
void AppendBody(std::string *output) { … }
void AppendFooter(std::string *output) { … }
#endif
}
AppendHeader;
AppendBody;
AppendFooter;
namespace {
bool CompareConfigInfos(const content::WebUIConfigInfo& config1,
const content::WebUIConfigInfo& config2) { … }
std::string ChromeURLs(content::BrowserContext* browser_context) { … }
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OPENBSD)
std::string AboutLinuxProxyConfig() { … }
#endif
}
AboutUIConfigBase::AboutUIConfigBase(std::string_view host)
: … { … }
ChromeURLsUIConfig::ChromeURLsUIConfig()
: … { … }
CreditsUIConfig::CreditsUIConfig()
: … { … }
#if !BUILDFLAG(IS_ANDROID)
TermsUIConfig::TermsUIConfig()
: … { … }
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_OPENBSD)
LinuxProxyConfigUI::LinuxProxyConfigUI()
: … { … }
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
OSCreditsUI::OSCreditsUI()
: AboutUIConfigBase(chrome::kChromeUIOSCreditsHost) {}
BorealisCreditsUI::BorealisCreditsUI()
: AboutUIConfigBase(chrome::kChromeUIBorealisCreditsHost) {}
CrostiniCreditsUI::CrostiniCreditsUI()
: AboutUIConfigBase(chrome::kChromeUICrostiniCreditsHost) {}
#endif
AboutUIHTMLSource::AboutUIHTMLSource(const std::string& source_name,
Profile* profile)
: … { … }
AboutUIHTMLSource::~AboutUIHTMLSource() { … }
std::string AboutUIHTMLSource::GetSource() { … }
void AboutUIHTMLSource::StartDataRequest(
const GURL& url,
const content::WebContents::Getter& wc_getter,
content::URLDataSource::GotDataCallback callback) { … }
void AboutUIHTMLSource::FinishDataRequest(
const std::string& html,
content::URLDataSource::GotDataCallback callback) { … }
std::string AboutUIHTMLSource::GetMimeType(const GURL& url) { … }
std::string AboutUIHTMLSource::GetAccessControlAllowOriginForOrigin(
const std::string& origin) { … }
AboutUI::AboutUI(content::WebUI* web_ui, const GURL& url)
: … { … }
#if BUILDFLAG(IS_CHROMEOS)
bool AboutUI::OverrideHandleWebUIMessage(const GURL& source_url,
const std::string& message,
const base::Value::List& args) {
if (message != "crosUrlAboutRedirect")
return false;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
lacros_url_handling::NavigateInAsh(GURL(chrome::kChromeUIAboutURL));
#else
DCHECK(crosapi::BrowserManager::Get());
crosapi::BrowserManager::Get()->SwitchToTab(
GURL(chrome::kChromeUIAboutURL),
NavigateParams::RESPECT);
#endif
return true;
}
#endif