ladybird/Ladybird/Android/src/main/cpp/LadybirdActivity.cpp

198 lines
7.4 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ALooperEventLoopImplementation.h"
#include <AK/DeprecatedString.h>
#include <AK/Format.h>
#include <AK/HashMap.h>
#include <AK/LexicalPath.h>
#include <AK/OwnPtr.h>
#include <Ladybird/Utilities.h>
#include <LibArchive/TarStream.h>
#include <LibCore/DirIterator.h>
#include <LibCore/Directory.h>
#include <LibCore/EventLoop.h>
#include <LibCore/System.h>
#include <LibCore/Timer.h>
#include <LibFileSystem/FileSystem.h>
#include <jni.h>
static ErrorOr<void> extract_tar_archive(String archive_file, DeprecatedString output_directory);
static OwnPtr<Core::EventLoop> s_main_event_loop;
extern "C" JNIEXPORT void JNICALL
Java_org_serenityos_ladybird_LadybirdActivity_initNativeCode(JNIEnv* env, jobject /* thiz */, jstring resource_dir, jstring tag_name, jobject timer_service)
{
char const* raw_resource_dir = env->GetStringUTFChars(resource_dir, nullptr);
s_serenity_resource_root = raw_resource_dir;
env->ReleaseStringUTFChars(resource_dir, raw_resource_dir);
char const* raw_tag_name = env->GetStringUTFChars(tag_name, nullptr);
AK::set_log_tag_name(raw_tag_name);
env->ReleaseStringUTFChars(tag_name, raw_tag_name);
dbgln("Set resource dir to {}", s_serenity_resource_root);
auto file_or_error = Core::System::open(MUST(String::formatted("{}/res/icons/16x16/app-browser.png", s_serenity_resource_root)), O_RDONLY);
if (file_or_error.is_error()) {
dbgln("No resource files, extracting assets...");
MUST(extract_tar_archive(MUST(String::formatted("{}/ladybird-assets.tar", s_serenity_resource_root)), s_serenity_resource_root));
} else {
dbgln("Found app-browser.png, not re-extracting assets.");
dbgln("Hopefully no developer changed the asset files and expected them to be re-extracted!");
}
jobject timer_service_ref = env->NewGlobalRef(timer_service);
JavaVM* vm = nullptr;
jint ret = env->GetJavaVM(&vm);
VERIFY(ret == 0);
Core::EventLoopManager::install(*new Ladybird::ALooperEventLoopManager(vm, timer_service_ref));
s_main_event_loop = make<Core::EventLoop>();
}
extern "C" JNIEXPORT void JNICALL
Java_org_serenityos_ladybird_LadybirdActivity_execMainEventLoop(JNIEnv*, jobject /* thiz */)
{
s_main_event_loop->pump(Core::EventLoop::WaitMode::PollForEvents);
}
ErrorOr<void> extract_tar_archive(String archive_file, DeprecatedString output_directory)
{
constexpr size_t buffer_size = 4096;
auto file = TRY(Core::InputBufferedFile::create(TRY(Core::File::open(archive_file, Core::File::OpenMode::Read))));
DeprecatedString old_pwd = TRY(Core::System::getcwd());
TRY(Core::System::chdir(output_directory));
ScopeGuard go_back = [&old_pwd] { MUST(Core::System::chdir(old_pwd)); };
auto tar_stream = TRY(Archive::TarInputStream::construct(move(file)));
HashMap<DeprecatedString, DeprecatedString> global_overrides;
HashMap<DeprecatedString, DeprecatedString> local_overrides;
auto get_override = [&](StringView key) -> Optional<DeprecatedString> {
Optional<DeprecatedString> maybe_local = local_overrides.get(key);
if (maybe_local.has_value())
return maybe_local;
Optional<DeprecatedString> maybe_global = global_overrides.get(key);
if (maybe_global.has_value())
return maybe_global;
return {};
};
while (!tar_stream->finished()) {
Archive::TarFileHeader const& header = tar_stream->header();
// Handle meta-entries earlier to avoid consuming the file content stream.
if (header.content_is_like_extended_header()) {
switch (header.type_flag()) {
case Archive::TarFileType::GlobalExtendedHeader: {
TRY(tar_stream->for_each_extended_header([&](StringView key, StringView value) {
if (value.length() == 0)
global_overrides.remove(key);
else
global_overrides.set(key, value);
}));
break;
}
case Archive::TarFileType::ExtendedHeader: {
TRY(tar_stream->for_each_extended_header([&](StringView key, StringView value) {
local_overrides.set(key, value);
}));
break;
}
default:
warnln("Unknown extended header type '{}' of {}", (char)header.type_flag(), header.filename());
VERIFY_NOT_REACHED();
}
TRY(tar_stream->advance());
continue;
}
Archive::TarFileStream file_stream = tar_stream->file_contents();
// Handle other header types that don't just have an effect on extraction.
switch (header.type_flag()) {
case Archive::TarFileType::LongName: {
StringBuilder long_name;
Array<u8, buffer_size> buffer;
while (!file_stream.is_eof()) {
auto slice = TRY(file_stream.read_some(buffer));
long_name.append(reinterpret_cast<char*>(slice.data()), slice.size());
}
local_overrides.set("path", long_name.to_deprecated_string());
TRY(tar_stream->advance());
continue;
}
default:
// None of the relevant headers, so continue as normal.
break;
}
LexicalPath path = LexicalPath(header.filename());
if (!header.prefix().is_empty())
path = path.prepend(header.prefix());
DeprecatedString filename = get_override("path"sv).value_or(path.string());
DeprecatedString absolute_path = TRY(FileSystem::absolute_path(filename)).to_deprecated_string();
auto parent_path = LexicalPath(absolute_path).parent();
auto header_mode = TRY(header.mode());
switch (header.type_flag()) {
case Archive::TarFileType::NormalFile:
case Archive::TarFileType::AlternateNormalFile: {
MUST(Core::Directory::create(parent_path, Core::Directory::CreateDirectories::Yes));
int fd = TRY(Core::System::open(absolute_path, O_CREAT | O_WRONLY, header_mode));
Array<u8, buffer_size> buffer;
while (!file_stream.is_eof()) {
auto slice = TRY(file_stream.read_some(buffer));
TRY(Core::System::write(fd, slice));
}
TRY(Core::System::close(fd));
break;
}
case Archive::TarFileType::SymLink: {
MUST(Core::Directory::create(parent_path, Core::Directory::CreateDirectories::Yes));
TRY(Core::System::symlink(header.link_name(), absolute_path));
break;
}
case Archive::TarFileType::Directory: {
MUST(Core::Directory::create(parent_path, Core::Directory::CreateDirectories::Yes));
auto result_or_error = Core::System::mkdir(absolute_path, header_mode);
if (result_or_error.is_error() && result_or_error.error().code() != EEXIST)
return result_or_error.release_error();
break;
}
default:
// FIXME: Implement other file types
warnln("file type '{}' of {} is not yet supported", (char)header.type_flag(), header.filename());
VERIFY_NOT_REACHED();
}
// Non-global headers should be cleared after every file.
local_overrides.clear();
TRY(tar_stream->advance());
}
return {};
}