blob: 297d2259ef4ae0aaa36249ab29295c00a32c939e [file] [log] [blame]
// Copyright 2019 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 "chrome/browser/chromeos/web_applications/terminal_source.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/chromeos/crostini/crostini_terminal.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "components/prefs/pref_service.h"
#include "net/base/escape.h"
#include "net/base/mime_util.h"
#include "third_party/zlib/google/compression_utils.h"
namespace {
// TODO(crbug.com/846546): Initially set to load crosh, but change to
// terminal when it is available.
constexpr base::FilePath::CharType kTerminalRoot[] =
FILE_PATH_LITERAL("/usr/share/chromeos-assets/crosh_builtin");
constexpr base::FilePath::CharType kDefaultFile[] =
FILE_PATH_LITERAL("html/crosh.html");
constexpr char kDefaultMime[] = "text/html";
void ReadFile(const std::string& relative_path,
content::URLDataSource::GotDataCallback callback) {
std::string content;
base::FilePath path = base::FilePath(kTerminalRoot).Append(relative_path);
// First look for uncompressed resource, then try for gzipped file.
bool result = base::ReadFileToString(path, &content);
if (!result) {
result =
base::ReadFileToString(base::FilePath(path.value() + ".gz"), &content);
std::string uncompressed;
result = compression::GzipUncompress(content, &uncompressed);
content = std::move(uncompressed);
}
// Terminal gets files from /usr/share/chromeos-assets/crosh-builtin.
// In chromium tests, these files don't exist, so we serve dummy values.
if (!result) {
static const base::NoDestructor<base::flat_map<std::string, std::string>>
kTestFiles({
{"html/pwa.html",
"<html><head><link rel='manifest' "
"href='/manifest.json'></head></html>"},
{"manifest.json", R"({
"name": "Test Terminal",
"icons": [{ "src": "/icon.svg", "sizes": "any" }],
"start_url": "/html/terminal.html"})"},
{"icon.svg",
"<svg xmlns='http://www.w3.org/2000/svg'><rect "
"fill='red'/></svg>"},
{"html/terminal.html", "<script src='/js/terminal.js'></script>"},
{"js/terminal.js",
"chrome.terminalPrivate.openVmshellProcess([], () => {})"},
});
auto it = kTestFiles->find(relative_path);
if (it != kTestFiles->end()) {
content = it->second;
result = true;
}
}
DCHECK(result) << path;
scoped_refptr<base::RefCountedString> response =
base::RefCountedString::TakeString(&content);
std::move(callback).Run(response.get());
}
} // namespace
TerminalSource::TerminalSource(Profile* profile) : profile_(profile) {}
TerminalSource::~TerminalSource() = default;
std::string TerminalSource::GetSource() {
return chrome::kChromeUIUntrustedTerminalURL;
}
#if !BUILDFLAG(OPTIMIZE_WEBUI)
bool TerminalSource::AllowCaching() {
return false;
}
#endif
void TerminalSource::StartDataRequest(
const GURL& url,
const content::WebContents::Getter& wc_getter,
content::URLDataSource::GotDataCallback callback) {
// skip first '/' in path.
std::string path = url.path().substr(1);
if (path.empty())
path = kDefaultFile;
// Replace $i8n{themeColor} in *.html.
if (base::EndsWith(path, ".html", base::CompareCase::INSENSITIVE_ASCII)) {
replacements_["themeColor"] = net::EscapeForHTML(
crostini::GetTerminalSettingBackgroundColor(profile_));
}
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
base::BindOnce(&ReadFile, path, std::move(callback)));
}
std::string TerminalSource::GetMimeType(const std::string& path) {
std::string mime_type(kDefaultMime);
std::string ext = base::FilePath(path).Extension();
if (!ext.empty())
net::GetWellKnownMimeTypeFromExtension(ext.substr(1), &mime_type);
return mime_type;
}
bool TerminalSource::ShouldServeMimeTypeAsContentTypeHeader() {
// TerminalSource pages include js modules which require an explicit MimeType.
return true;
}
const ui::TemplateReplacements* TerminalSource::GetReplacements() {
return &replacements_;
}