Everywhere: Gently remove the ladybird android port
With Ladybird now being its own repository, there's little reason to keep the Ladybird Android port in the SerenityOS repository. (The Qt port is useful to be able to test changes to LibWeb in lagom so it'll stay around. Similar for the AppKit port, since getting Qt on macOS is a bit annoying. But if the AppKit port is too much pain to keep working, we should toss that too. Eventually, the lagom browser ports should move out from Ladybird/ to Meta/Lagom/Contrib, but for now it might make sense to leave them where they are to keep cherry-picks from ladybird easier.)
|
@ -10,17 +10,11 @@
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
|
|
||||||
#if (defined(AK_OS_LINUX) && !defined(AK_OS_ANDROID)) || defined(AK_LIBC_GLIBC) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_SOLARIS) || defined(AK_OS_HAIKU)
|
#if defined(AK_OS_LINUX) || defined(AK_LIBC_GLIBC) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_SOLARIS) || defined(AK_OS_HAIKU)
|
||||||
# define EXECINFO_BACKTRACE
|
# define EXECINFO_BACKTRACE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(AK_OS_ANDROID) && (__ANDROID_API__ >= 33)
|
#define PRINT_ERROR(s) (void)fputs((s), stderr)
|
||||||
# include <android/log.h>
|
|
||||||
# define EXECINFO_BACKTRACE
|
|
||||||
# define PRINT_ERROR(s) __android_log_write(ANDROID_LOG_WARN, "AK", (s))
|
|
||||||
#else
|
|
||||||
# define PRINT_ERROR(s) (void)fputs((s), stderr)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(EXECINFO_BACKTRACE)
|
#if defined(EXECINFO_BACKTRACE)
|
||||||
# include <cxxabi.h>
|
# include <cxxabi.h>
|
||||||
|
@ -74,9 +68,7 @@ ALWAYS_INLINE void dump_backtrace()
|
||||||
} else {
|
} else {
|
||||||
error_builder.append(sym);
|
error_builder.append(sym);
|
||||||
}
|
}
|
||||||
# if !defined(AK_OS_ANDROID)
|
|
||||||
error_builder.append('\n');
|
error_builder.append('\n');
|
||||||
# endif
|
|
||||||
error_builder.append('\0');
|
error_builder.append('\0');
|
||||||
PRINT_ERROR(error_builder.string_view().characters_without_null_termination());
|
PRINT_ERROR(error_builder.string_view().characters_without_null_termination());
|
||||||
}
|
}
|
||||||
|
@ -89,7 +81,7 @@ extern "C" {
|
||||||
|
|
||||||
void ak_verification_failed(char const* message)
|
void ak_verification_failed(char const* message)
|
||||||
{
|
{
|
||||||
# if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID)
|
# if defined(AK_OS_SERENITY)
|
||||||
bool colorize_output = true;
|
bool colorize_output = true;
|
||||||
# elif defined(AK_OS_WINDOWS)
|
# elif defined(AK_OS_WINDOWS)
|
||||||
bool colorize_output = false;
|
bool colorize_output = false;
|
||||||
|
|
|
@ -28,10 +28,6 @@
|
||||||
# include <time.h>
|
# include <time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(AK_OS_ANDROID)
|
|
||||||
# include <android/log.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef KERNEL
|
#ifndef KERNEL
|
||||||
# include <AK/StringFloatingPointConversions.h>
|
# include <AK/StringFloatingPointConversions.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -1090,45 +1086,6 @@ void vout(FILE* file, StringView fmtstr, TypeErasedFormatParams& params, bool ne
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef AK_OS_ANDROID
|
|
||||||
static char const* s_log_tag_name = "Serenity";
|
|
||||||
void set_log_tag_name(char const* tag_name)
|
|
||||||
{
|
|
||||||
static String s_log_tag_storage;
|
|
||||||
// NOTE: Make sure to copy the null terminator
|
|
||||||
s_log_tag_storage = MUST(String::from_utf8({ tag_name, strlen(tag_name) + 1 }));
|
|
||||||
s_log_tag_name = s_log_tag_storage.bytes_as_string_view().characters_without_null_termination();
|
|
||||||
}
|
|
||||||
|
|
||||||
void vout(LogLevel log_level, StringView fmtstr, TypeErasedFormatParams& params, bool newline)
|
|
||||||
{
|
|
||||||
StringBuilder builder;
|
|
||||||
MUST(vformat(builder, fmtstr, params));
|
|
||||||
|
|
||||||
if (newline)
|
|
||||||
builder.append('\n');
|
|
||||||
builder.append('\0');
|
|
||||||
|
|
||||||
auto const string = builder.string_view();
|
|
||||||
|
|
||||||
auto ndk_log_level = ANDROID_LOG_UNKNOWN;
|
|
||||||
switch (log_level) {
|
|
||||||
case LogLevel ::Debug:
|
|
||||||
ndk_log_level = ANDROID_LOG_DEBUG;
|
|
||||||
break;
|
|
||||||
case LogLevel ::Info:
|
|
||||||
ndk_log_level = ANDROID_LOG_INFO;
|
|
||||||
break;
|
|
||||||
case LogLevel::Warning:
|
|
||||||
ndk_log_level = ANDROID_LOG_WARN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
__android_log_write(ndk_log_level, s_log_tag_name, string.characters_without_null_termination());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef KERNEL
|
#ifndef KERNEL
|
||||||
// FIXME: Deduplicate with Core::Process:get_name()
|
// FIXME: Deduplicate with Core::Process:get_name()
|
||||||
[[gnu::used]] static ByteString process_name_helper()
|
[[gnu::used]] static ByteString process_name_helper()
|
||||||
|
@ -1139,7 +1096,7 @@ void vout(LogLevel log_level, StringView fmtstr, TypeErasedFormatParams& params,
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
return ByteString {};
|
return ByteString {};
|
||||||
return StringView { buffer, strlen(buffer) };
|
return StringView { buffer, strlen(buffer) };
|
||||||
# elif defined(AK_LIBC_GLIBC) || (defined(AK_OS_LINUX) && !defined(AK_OS_ANDROID))
|
# elif defined(AK_LIBC_GLIBC) || defined(AK_OS_LINUX)
|
||||||
return StringView { program_invocation_name, strlen(program_invocation_name) };
|
return StringView { program_invocation_name, strlen(program_invocation_name) };
|
||||||
# elif defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU)
|
# elif defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU)
|
||||||
auto const* progname = getprogname();
|
auto const* progname = getprogname();
|
||||||
|
@ -1235,9 +1192,6 @@ void vdbg(StringView fmtstr, TypeErasedFormatParams& params, bool newline)
|
||||||
MUST(vformat(builder, fmtstr, params));
|
MUST(vformat(builder, fmtstr, params));
|
||||||
if (newline)
|
if (newline)
|
||||||
builder.append('\n');
|
builder.append('\n');
|
||||||
#ifdef AK_OS_ANDROID
|
|
||||||
builder.append('\0');
|
|
||||||
#endif
|
|
||||||
auto const string = builder.string_view();
|
auto const string = builder.string_view();
|
||||||
|
|
||||||
#ifdef AK_OS_SERENITY
|
#ifdef AK_OS_SERENITY
|
||||||
|
@ -1248,11 +1202,7 @@ void vdbg(StringView fmtstr, TypeErasedFormatParams& params, bool newline)
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef AK_OS_ANDROID
|
|
||||||
__android_log_write(ANDROID_LOG_DEBUG, s_log_tag_name, string.characters_without_null_termination());
|
|
||||||
#else
|
|
||||||
dbgputstr(string.characters_without_null_termination(), string.length());
|
dbgputstr(string.characters_without_null_termination(), string.length());
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KERNEL
|
#ifdef KERNEL
|
||||||
|
|
44
AK/Format.h
|
@ -592,7 +592,6 @@ void outln(FILE* file, CheckedFormatString<Parameters...>&& fmtstr, Parameters c
|
||||||
|
|
||||||
inline void outln(FILE* file) { fputc('\n', file); }
|
inline void outln(FILE* file) { fputc('\n', file); }
|
||||||
|
|
||||||
# ifndef AK_OS_ANDROID
|
|
||||||
template<typename... Parameters>
|
template<typename... Parameters>
|
||||||
void out(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
void out(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
||||||
{
|
{
|
||||||
|
@ -614,49 +613,6 @@ template<typename... Parameters>
|
||||||
void warnln(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters) { outln(stderr, move(fmtstr), parameters...); }
|
void warnln(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters) { outln(stderr, move(fmtstr), parameters...); }
|
||||||
|
|
||||||
inline void warnln() { outln(stderr); }
|
inline void warnln() { outln(stderr); }
|
||||||
# else // v Android ^ No Android
|
|
||||||
enum class LogLevel {
|
|
||||||
Debug,
|
|
||||||
Info,
|
|
||||||
Warning,
|
|
||||||
};
|
|
||||||
|
|
||||||
void vout(LogLevel, StringView fmtstr, TypeErasedFormatParams&, bool newline = false);
|
|
||||||
|
|
||||||
template<typename... Parameters>
|
|
||||||
void out(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
|
||||||
{
|
|
||||||
VariadicFormatParams<AllowDebugOnlyFormatters::Yes, Parameters...> variadic_format_params { parameters... };
|
|
||||||
vout(LogLevel::Info, fmtstr.view(), variadic_format_params);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Parameters>
|
|
||||||
void outln(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
|
||||||
{
|
|
||||||
VariadicFormatParams<AllowDebugOnlyFormatters::Yes, Parameters...> variadic_format_params { parameters... };
|
|
||||||
vout(LogLevel::Info, fmtstr.view(), variadic_format_params, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void outln() { outln(""); }
|
|
||||||
|
|
||||||
template<typename... Parameters>
|
|
||||||
void warn(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
|
||||||
{
|
|
||||||
VariadicFormatParams<AllowDebugOnlyFormatters::Yes, Parameters...> variadic_format_params { parameters... };
|
|
||||||
vout(LogLevel::Warning, fmtstr.view(), variadic_format_params);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Parameters>
|
|
||||||
void warnln(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
|
|
||||||
{
|
|
||||||
VariadicFormatParams<AllowDebugOnlyFormatters::Yes, Parameters...> variadic_format_params { parameters... };
|
|
||||||
vout(LogLevel::Warning, fmtstr.view(), variadic_format_params, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void warnln() { warnln(""); }
|
|
||||||
|
|
||||||
void set_log_tag_name(char const*);
|
|
||||||
# endif // AK_OS_ANDROID
|
|
||||||
|
|
||||||
# define outln_if(flag, fmt, ...) \
|
# define outln_if(flag, fmt, ...) \
|
||||||
do { \
|
do { \
|
||||||
|
|
|
@ -127,18 +127,6 @@
|
||||||
# define AK_OS_WINDOWS
|
# define AK_OS_WINDOWS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
# define STR(x) __STR(x)
|
|
||||||
# define __STR(x) #x
|
|
||||||
# if __ANDROID_API__ < 30
|
|
||||||
# pragma message "Invalid android API " STR(__ANDROID_API__)
|
|
||||||
# error "Build configuration not tested on configured Android API version"
|
|
||||||
# endif
|
|
||||||
# undef STR
|
|
||||||
# undef __STR
|
|
||||||
# define AK_OS_ANDROID
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__EMSCRIPTEN__)
|
#if defined(__EMSCRIPTEN__)
|
||||||
# define AK_OS_EMSCRIPTEN
|
# define AK_OS_EMSCRIPTEN
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace AK {
|
||||||
|
|
||||||
inline void fill_with_random([[maybe_unused]] Bytes bytes)
|
inline void fill_with_random([[maybe_unused]] Bytes bytes)
|
||||||
{
|
{
|
||||||
#if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU) || AK_LIBC_GLIBC_PREREQ(2, 36)
|
#if defined(AK_OS_SERENITY) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU) || AK_LIBC_GLIBC_PREREQ(2, 36)
|
||||||
arc4random_buf(bytes.data(), bytes.size());
|
arc4random_buf(bytes.data(), bytes.size());
|
||||||
#elif defined(OSS_FUZZ)
|
#elif defined(OSS_FUZZ)
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -97,7 +97,7 @@ StackInfo::StackInfo()
|
||||||
|
|
||||||
m_top = m_base + m_size;
|
m_top = m_base + m_size;
|
||||||
|
|
||||||
#if defined(AK_OS_LINUX) && !defined(AK_OS_ANDROID) && !defined(AK_LIBC_GLIBC)
|
#if defined(AK_OS_LINUX) && !defined(AK_LIBC_GLIBC)
|
||||||
// Note: musl libc always gives the initial size of the main thread's stack
|
// Note: musl libc always gives the initial size of the main thread's stack
|
||||||
if (getpid() == static_cast<pid_t>(gettid())) {
|
if (getpid() == static_cast<pid_t>(gettid())) {
|
||||||
rlimit limit;
|
rlimit limit;
|
||||||
|
|
|
@ -28,8 +28,7 @@ namespace AK {
|
||||||
|
|
||||||
// FIXME: Remove this when OpenBSD Clang fully supports consteval.
|
// FIXME: Remove this when OpenBSD Clang fully supports consteval.
|
||||||
// And once oss-fuzz updates to clang >15.
|
// And once oss-fuzz updates to clang >15.
|
||||||
// And once Android ships an NDK with clang >14
|
#if defined(AK_OS_OPENBSD) || defined(OSS_FUZZ)
|
||||||
#if defined(AK_OS_OPENBSD) || defined(OSS_FUZZ) || defined(AK_OS_ANDROID)
|
|
||||||
# define AK_SHORT_STRING_CONSTEVAL constexpr
|
# define AK_SHORT_STRING_CONSTEVAL constexpr
|
||||||
#else
|
#else
|
||||||
# define AK_SHORT_STRING_CONSTEVAL consteval
|
# define AK_SHORT_STRING_CONSTEVAL consteval
|
||||||
|
|
|
@ -408,8 +408,7 @@ struct CaseInsensitiveASCIIStringViewTraits : public Traits<StringView> {
|
||||||
// See: https://github.com/llvm/llvm-project/issues/48230
|
// See: https://github.com/llvm/llvm-project/issues/48230
|
||||||
// Additionally, oss-fuzz currently ships an llvm-project commit that is a pre-release of 15.0.0.
|
// Additionally, oss-fuzz currently ships an llvm-project commit that is a pre-release of 15.0.0.
|
||||||
// See: https://github.com/google/oss-fuzz/issues/9989
|
// See: https://github.com/google/oss-fuzz/issues/9989
|
||||||
// Android currently doesn't ship clang-15 in any NDK
|
#if defined(AK_OS_BSD_GENERIC) || defined(OSS_FUZZ)
|
||||||
#if defined(AK_OS_BSD_GENERIC) || defined(OSS_FUZZ) || defined(AK_OS_ANDROID)
|
|
||||||
# define AK_STRING_VIEW_LITERAL_CONSTEVAL constexpr
|
# define AK_STRING_VIEW_LITERAL_CONSTEVAL constexpr
|
||||||
#else
|
#else
|
||||||
# define AK_STRING_VIEW_LITERAL_CONSTEVAL consteval
|
# define AK_STRING_VIEW_LITERAL_CONSTEVAL consteval
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
## Android Studio Project Configuration
|
|
||||||
|
|
||||||
The Android port of Ladybird has straightforward integration with the Android Studio IDE.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
Ensure that your system has the following tools available:
|
|
||||||
|
|
||||||
- Android Studio Jellyfish 2023.3.1 or later
|
|
||||||
- CMake 3.23 or higher as the default CMake executable
|
|
||||||
- 20G or more storage space for SDKs + Emulator images + Gradle dependencies + build artifacts
|
|
||||||
|
|
||||||
## Opening the project
|
|
||||||
|
|
||||||
After opening the ``serenity`` directory in Android Studio (NOT the Ladybird/Android directory!)
|
|
||||||
there should be a pop-up in the bottom left indicating that an Android Gradle project was detected
|
|
||||||
in ``Ladybird/Android``.
|
|
||||||
|
|
||||||
In the top left of the screen in the Project view, navigate to ``Ladybird/Android``. Or, click the
|
|
||||||
highlighted text in the notification for that path. Open the ``settings.gradle.kts`` file. At the
|
|
||||||
top of the file should be a banner that says ``Code Insight unavailable (related Gradle project not
|
|
||||||
linked).`` Click the ``Link Gradle project`` text on the right side of the banner. After the IDE
|
|
||||||
loads the Gradle view to the right of the code window, go back to the banner at the top of the
|
|
||||||
``settings.gradle.kts`` file and click ``Load Script Configurations`` to finish loading the Gradle
|
|
||||||
project.
|
|
||||||
|
|
||||||
Gradle will index the project, and download all the required plugins. If it complains about no NDK,
|
|
||||||
follow the instructions in Android Studio to install an appropriate NDK version. If it still
|
|
||||||
complains about the NDK version, open ``File->Invalidate Caches...`` and click ``Invalidate and
|
|
||||||
Restart``.
|
|
||||||
|
|
||||||
## Getting the most out of the IDE
|
|
||||||
|
|
||||||
See the sections in the [CLionConfiguration](CLionConfiguration.md) for [Excluding Build Artifacts](CLionConfiguration.md#excluding-build-artifacts),
|
|
||||||
and [Code Generation Settings](CLionConfiguration.md#code-generation-settings).
|
|
|
@ -84,11 +84,6 @@ On Windows:
|
||||||
WSL2/WSLg are preferred, as they provide a linux environment that matches one of the above distributions.
|
WSL2/WSLg are preferred, as they provide a linux environment that matches one of the above distributions.
|
||||||
MinGW/MSYS2 are not supported, but may work with sufficient elbow grease. Native Windows builds are not supported with either clang-cl or MSVC.
|
MinGW/MSYS2 are not supported, but may work with sufficient elbow grease. Native Windows builds are not supported with either clang-cl or MSVC.
|
||||||
|
|
||||||
For Android:
|
|
||||||
|
|
||||||
On a Unix-like platform, install the prerequisites for that platform and then see the [Android Studio guide](AndroidStudioConfiguration.md).
|
|
||||||
Or, download a version of Gradle >= 8.0.0, and run the ``gradlew`` program in ``Ladybird/Android``
|
|
||||||
|
|
||||||
## Build steps
|
## Build steps
|
||||||
|
|
||||||
### Using serenity.sh
|
### Using serenity.sh
|
||||||
|
@ -102,11 +97,10 @@ The simplest way to build and run ladybird is via the serenity.sh script:
|
||||||
```
|
```
|
||||||
|
|
||||||
The above commands will build Ladybird with one of the following browser chromes, depending on the platform:
|
The above commands will build Ladybird with one of the following browser chromes, depending on the platform:
|
||||||
* [Android UI](https://developer.android.com/develop/ui) - The native chrome on Android.
|
|
||||||
* [AppKit](https://developer.apple.com/documentation/appkit?language=objc) - The native chrome on macOS.
|
* [AppKit](https://developer.apple.com/documentation/appkit?language=objc) - The native chrome on macOS.
|
||||||
* [Qt](https://doc.qt.io/qt-6/) - The chrome used on all other platforms.
|
* [Qt](https://doc.qt.io/qt-6/) - The chrome used on all other platforms.
|
||||||
|
|
||||||
The Qt chrome is available on platforms where it is not the default as well (except on Android). To build the
|
The Qt chrome is available on platforms where it is not the default as well. To build the
|
||||||
Qt chrome, install the Qt dependencies for your platform, and enable the Qt chrome via CMake:
|
Qt chrome, install the Qt dependencies for your platform, and enable the Qt chrome via CMake:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
1
Ladybird/.gitignore
vendored
|
@ -5,4 +5,3 @@ moc_*
|
||||||
Build
|
Build
|
||||||
build
|
build
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
Android/src/main/assets/
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -eo pipefail
|
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
SERENITY_ROOT="$(realpath "${DIR}"/../..)"
|
|
||||||
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
. "${SERENITY_ROOT}/Meta/shell_include.sh"
|
|
||||||
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
. "${SERENITY_ROOT}/Meta/find_compiler.sh"
|
|
||||||
|
|
||||||
pick_host_compiler
|
|
||||||
|
|
||||||
BUILD_DIR=${BUILD_DIR:-"${SERENITY_ROOT}/Build"}
|
|
||||||
CACHE_DIR=${CACHE_DIR:-"${BUILD_DIR}/caches"}
|
|
||||||
|
|
||||||
cmake -S "$SERENITY_ROOT/Meta/Lagom" -B "$BUILD_DIR/lagom-tools" \
|
|
||||||
-GNinja -Dpackage=LagomTools \
|
|
||||||
-DCMAKE_INSTALL_PREFIX="$BUILD_DIR/lagom-tools-install" \
|
|
||||||
-DCMAKE_C_COMPILER="$CC" \
|
|
||||||
-DCMAKE_CXX_COMPILER="$CXX" \
|
|
||||||
-DSERENITY_CACHE_DIR="$CACHE_DIR"
|
|
||||||
|
|
||||||
ninja -C "$BUILD_DIR/lagom-tools" install
|
|
|
@ -1,88 +0,0 @@
|
||||||
import com.android.build.gradle.internal.tasks.factory.dependsOn
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id("com.android.application") version "8.4.0"
|
|
||||||
id("org.jetbrains.kotlin.android") version "1.9.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
var buildDir = layout.buildDirectory.get()
|
|
||||||
var cacheDir = System.getenv("SERENITY_CACHE_DIR") ?: "$buildDir/caches"
|
|
||||||
|
|
||||||
task<Exec>("buildLagomTools") {
|
|
||||||
commandLine = listOf("./BuildLagomTools.sh")
|
|
||||||
environment = mapOf(
|
|
||||||
"BUILD_DIR" to buildDir,
|
|
||||||
"CACHE_DIR" to cacheDir,
|
|
||||||
"PATH" to System.getenv("PATH")!!
|
|
||||||
)
|
|
||||||
}
|
|
||||||
tasks.named("preBuild").dependsOn("buildLagomTools")
|
|
||||||
tasks.named("prepareKotlinBuildScriptModel").dependsOn("buildLagomTools")
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "org.serenityos.ladybird"
|
|
||||||
compileSdk = 34
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId = "org.serenityos.ladybird"
|
|
||||||
minSdk = 30
|
|
||||||
targetSdk = 34
|
|
||||||
versionCode = 1
|
|
||||||
versionName = "1.0"
|
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
cppFlags += "-std=c++23"
|
|
||||||
arguments += listOf(
|
|
||||||
"-DLagomTools_DIR=$buildDir/lagom-tools-install/share/LagomTools",
|
|
||||||
"-DSERENITY_CACHE_DIR=$cacheDir"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ndk {
|
|
||||||
// Specifies the ABI configurations of your native
|
|
||||||
// libraries Gradle should build and package with your app.
|
|
||||||
abiFilters += listOf("x86_64", "arm64-v8a")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
isMinifyEnabled = false
|
|
||||||
proguardFiles(
|
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
||||||
"proguard-rules.pro"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "11"
|
|
||||||
}
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
path = file("../CMakeLists.txt")
|
|
||||||
version = "3.23.0+"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
viewBinding = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("androidx.core:core-ktx:1.13.1")
|
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
|
||||||
implementation("com.google.android.material:material:1.12.0")
|
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
|
||||||
testImplementation("junit:junit:4.13.2")
|
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
|
||||||
androidTestImplementation("androidx.test.ext:junit-ktx:1.1.5")
|
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Project-wide Gradle settings.
|
|
||||||
# IDE (e.g. Android Studio) users:
|
|
||||||
# Gradle settings configured through the IDE *will override*
|
|
||||||
# any settings specified in this file.
|
|
||||||
# For more details on how to configure your build environment visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
|
||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
|
||||||
# org.gradle.parallel=true
|
|
||||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
|
||||||
# Android operating system, and which are packaged with your app's APK
|
|
||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
|
||||||
android.useAndroidX=true
|
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
|
||||||
kotlin.code.style=official
|
|
||||||
# Enables namespacing of each library's R class so that its R class includes only the
|
|
||||||
# resources declared in the library itself and none from the library's dependencies,
|
|
||||||
# thereby reducing the size of the R class for that library
|
|
||||||
android.nonTransitiveRClass=true
|
|
BIN
Ladybird/Android/gradle/wrapper/gradle-wrapper.jar
vendored
|
@ -1,6 +0,0 @@
|
||||||
#Fri Sep 01 12:36:55 CEST 2023
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
185
Ladybird/Android/gradlew
vendored
|
@ -1,185 +0,0 @@
|
||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copyright 2015 the original author or authors.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
##
|
|
||||||
## Gradle start up script for UN*X
|
|
||||||
##
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
|
||||||
# Resolve links: $0 may be a link
|
|
||||||
PRG="$0"
|
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
|
||||||
ls=`ls -ld "$PRG"`
|
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
|
||||||
PRG="$link"
|
|
||||||
else
|
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=`basename "$0"`
|
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
|
||||||
MAX_FD="maximum"
|
|
||||||
|
|
||||||
warn () {
|
|
||||||
echo "$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
die () {
|
|
||||||
echo
|
|
||||||
echo "$*"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
|
||||||
cygwin=false
|
|
||||||
msys=false
|
|
||||||
darwin=false
|
|
||||||
nonstop=false
|
|
||||||
case "`uname`" in
|
|
||||||
CYGWIN* )
|
|
||||||
cygwin=true
|
|
||||||
;;
|
|
||||||
Darwin* )
|
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
|
||||||
else
|
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
|
||||||
fi
|
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
location of your Java installation."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
JAVACMD="java"
|
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
location of your Java installation."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
|
||||||
if [ $? -eq 0 ] ; then
|
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
ulimit -n $MAX_FD
|
|
||||||
if [ $? -ne 0 ] ; then
|
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Escape application args
|
|
||||||
save () {
|
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
|
||||||
echo " "
|
|
||||||
}
|
|
||||||
APP_ARGS=`save "$@"`
|
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
|
21
Ladybird/Android/proguard-rules.pro
vendored
|
@ -1,21 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
|
@ -1,16 +0,0 @@
|
||||||
pluginManagement {
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dependencyResolutionManagement {
|
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.name = "Ladybird"
|
|
|
@ -1,40 +0,0 @@
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.rules.activityScenarioRule
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
import org.junit.Assert.*
|
|
||||||
import org.junit.Rule
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumented test, which will execute on an Android device.
|
|
||||||
*
|
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class SmokeTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
var activityScenarioRule = activityScenarioRule<LadybirdActivity>()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun useAppContext() {
|
|
||||||
// Context of the app under test.
|
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
assertEquals("org.serenityos.ladybird", appContext.packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun loadWebView() {
|
|
||||||
// We can actually load a web view, and it is visible
|
|
||||||
onView(withId(R.id.web_view)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:installLocation="auto"
|
|
||||||
android:versionCode="001"
|
|
||||||
android:versionName="head">
|
|
||||||
|
|
||||||
<supports-screens
|
|
||||||
android:anyDensity="true"
|
|
||||||
android:largeScreens="true"
|
|
||||||
android:normalScreens="true"
|
|
||||||
android:smallScreens="true" />
|
|
||||||
|
|
||||||
<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:allowBackup="true"
|
|
||||||
android:allowNativeHeapPointerTagging="false"
|
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
|
||||||
android:fullBackupOnly="false"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:supportsRtl="true"
|
|
||||||
android:theme="@style/Theme.Ladybird"
|
|
||||||
tools:targetApi="33">
|
|
||||||
<activity
|
|
||||||
android:name=".LadybirdActivity"
|
|
||||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
|
||||||
android:exported="true"
|
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:screenOrientation="unspecified">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.app.extract_android_style"
|
|
||||||
android:value="minimal" />
|
|
||||||
</activity>
|
|
||||||
<service
|
|
||||||
android:name=".WebContentService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="false"
|
|
||||||
android:process=":WebContent" />
|
|
||||||
<service
|
|
||||||
android:name=".RequestServerService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="false"
|
|
||||||
android:process=":RequestServer" />
|
|
||||||
<service
|
|
||||||
android:name=".ImageDecoderService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="false"
|
|
||||||
android:process=":ImageDecoder" />
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
|
@ -1,259 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ALooperEventLoopImplementation.h"
|
|
||||||
#include "JNIHelpers.h"
|
|
||||||
#include <LibCore/EventLoop.h>
|
|
||||||
#include <LibCore/Notifier.h>
|
|
||||||
#include <LibCore/ThreadEventQueue.h>
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <android/looper.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
namespace Ladybird {
|
|
||||||
|
|
||||||
EventLoopThreadData& EventLoopThreadData::the()
|
|
||||||
{
|
|
||||||
static thread_local EventLoopThreadData s_thread_data { {}, {}, &Core::ThreadEventQueue::current() };
|
|
||||||
return s_thread_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ALooperEventLoopImplementation& current_impl()
|
|
||||||
{
|
|
||||||
return verify_cast<ALooperEventLoopImplementation>(Core::EventLoop::current().impl());
|
|
||||||
}
|
|
||||||
|
|
||||||
static int looper_callback(int fd, int events, void* data);
|
|
||||||
|
|
||||||
ALooperEventLoopManager::ALooperEventLoopManager(jobject timer_service)
|
|
||||||
: m_timer_service(timer_service)
|
|
||||||
{
|
|
||||||
JavaEnvironment env(global_vm);
|
|
||||||
|
|
||||||
jclass timer_class = env.get()->FindClass("org/serenityos/ladybird/TimerExecutorService$Timer");
|
|
||||||
if (!timer_class)
|
|
||||||
TODO();
|
|
||||||
m_timer_class = reinterpret_cast<jclass>(env.get()->NewGlobalRef(timer_class));
|
|
||||||
env.get()->DeleteLocalRef(timer_class);
|
|
||||||
|
|
||||||
m_timer_constructor = env.get()->GetMethodID(m_timer_class, "<init>", "(J)V");
|
|
||||||
if (!m_timer_constructor)
|
|
||||||
TODO();
|
|
||||||
|
|
||||||
jclass timer_service_class = env.get()->GetObjectClass(m_timer_service);
|
|
||||||
|
|
||||||
m_register_timer = env.get()->GetMethodID(timer_service_class, "registerTimer", "(Lorg/serenityos/ladybird/TimerExecutorService$Timer;ZJ)J");
|
|
||||||
if (!m_register_timer)
|
|
||||||
TODO();
|
|
||||||
|
|
||||||
m_unregister_timer = env.get()->GetMethodID(timer_service_class, "unregisterTimer", "(J)V");
|
|
||||||
if (!m_unregister_timer)
|
|
||||||
TODO();
|
|
||||||
env.get()->DeleteLocalRef(timer_service_class);
|
|
||||||
|
|
||||||
auto ret = pipe2(m_pipe, O_CLOEXEC | O_NONBLOCK);
|
|
||||||
VERIFY(ret == 0);
|
|
||||||
|
|
||||||
m_main_looper = ALooper_forThread();
|
|
||||||
VERIFY(m_main_looper);
|
|
||||||
ALooper_acquire(m_main_looper);
|
|
||||||
|
|
||||||
ret = ALooper_addFd(m_main_looper, m_pipe[0], ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, &looper_callback, this);
|
|
||||||
VERIFY(ret == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ALooperEventLoopManager::~ALooperEventLoopManager()
|
|
||||||
{
|
|
||||||
JavaEnvironment env(global_vm);
|
|
||||||
|
|
||||||
env.get()->DeleteGlobalRef(m_timer_service);
|
|
||||||
env.get()->DeleteGlobalRef(m_timer_class);
|
|
||||||
|
|
||||||
ALooper_removeFd(m_main_looper, m_pipe[0]);
|
|
||||||
ALooper_release(m_main_looper);
|
|
||||||
|
|
||||||
::close(m_pipe[0]);
|
|
||||||
::close(m_pipe[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
NonnullOwnPtr<Core::EventLoopImplementation> ALooperEventLoopManager::make_implementation()
|
|
||||||
{
|
|
||||||
return ALooperEventLoopImplementation::create();
|
|
||||||
}
|
|
||||||
|
|
||||||
intptr_t ALooperEventLoopManager::register_timer(Core::EventReceiver& receiver, int milliseconds, bool should_reload, Core::TimerShouldFireWhenNotVisible visibility)
|
|
||||||
{
|
|
||||||
JavaEnvironment env(global_vm);
|
|
||||||
auto& thread_data = EventLoopThreadData::the();
|
|
||||||
|
|
||||||
auto timer = env.get()->NewObject(m_timer_class, m_timer_constructor, reinterpret_cast<long>(¤t_impl()));
|
|
||||||
|
|
||||||
long millis = milliseconds;
|
|
||||||
long timer_id = env.get()->CallLongMethod(m_timer_service, m_register_timer, timer, !should_reload, millis);
|
|
||||||
|
|
||||||
// FIXME: Is there a race condition here? Maybe we should take a lock on the timers...
|
|
||||||
thread_data.timers.set(timer_id, { receiver.make_weak_ptr(), visibility });
|
|
||||||
|
|
||||||
return timer_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopManager::unregister_timer(intptr_t timer_id)
|
|
||||||
{
|
|
||||||
if (auto timer = EventLoopThreadData::the().timers.take(timer_id); timer.has_value()) {
|
|
||||||
JavaEnvironment env(global_vm);
|
|
||||||
env.get()->CallVoidMethod(m_timer_service, m_unregister_timer, timer_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopManager::register_notifier(Core::Notifier& notifier)
|
|
||||||
{
|
|
||||||
EventLoopThreadData::the().notifiers.set(¬ifier);
|
|
||||||
current_impl().register_notifier(notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopManager::unregister_notifier(Core::Notifier& notifier)
|
|
||||||
{
|
|
||||||
EventLoopThreadData::the().notifiers.remove(¬ifier);
|
|
||||||
current_impl().unregister_notifier(notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopManager::did_post_event()
|
|
||||||
{
|
|
||||||
int msg = 0xCAFEBABE;
|
|
||||||
(void)write(m_pipe[1], &msg, sizeof(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
int looper_callback(int fd, int events, void* data)
|
|
||||||
{
|
|
||||||
auto& manager = *static_cast<ALooperEventLoopManager*>(data);
|
|
||||||
|
|
||||||
if (events & ALOOPER_EVENT_INPUT) {
|
|
||||||
int msg = 0;
|
|
||||||
while (read(fd, &msg, sizeof(msg)) == sizeof(msg)) {
|
|
||||||
// Do nothing, we don't actually care what the message was, just that it was posted
|
|
||||||
}
|
|
||||||
manager.on_did_post_event();
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALooperEventLoopImplementation::ALooperEventLoopImplementation()
|
|
||||||
: m_event_loop(ALooper_prepare(0))
|
|
||||||
, m_thread_data(&EventLoopThreadData::the())
|
|
||||||
{
|
|
||||||
ALooper_acquire(m_event_loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
ALooperEventLoopImplementation::~ALooperEventLoopImplementation()
|
|
||||||
{
|
|
||||||
ALooper_release(m_event_loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
EventLoopThreadData& ALooperEventLoopImplementation::thread_data()
|
|
||||||
{
|
|
||||||
return *m_thread_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ALooperEventLoopImplementation::exec()
|
|
||||||
{
|
|
||||||
while (!m_exit_requested.load(MemoryOrder::memory_order_acquire))
|
|
||||||
pump(PumpMode::WaitForEvents);
|
|
||||||
return m_exit_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ALooperEventLoopImplementation::pump(Core::EventLoopImplementation::PumpMode mode)
|
|
||||||
{
|
|
||||||
auto num_events = Core::ThreadEventQueue::current().process();
|
|
||||||
|
|
||||||
int timeout_ms = mode == Core::EventLoopImplementation::PumpMode::WaitForEvents ? -1 : 0;
|
|
||||||
auto ret = ALooper_pollAll(timeout_ms, nullptr, nullptr, nullptr);
|
|
||||||
|
|
||||||
// We don't expect any non-callback FDs to be ready
|
|
||||||
VERIFY(ret <= 0);
|
|
||||||
|
|
||||||
if (ret == ALOOPER_POLL_ERROR)
|
|
||||||
m_exit_requested.store(true, MemoryOrder::memory_order_release);
|
|
||||||
|
|
||||||
num_events += Core::ThreadEventQueue::current().process();
|
|
||||||
return num_events;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopImplementation::quit(int code)
|
|
||||||
{
|
|
||||||
m_exit_code = code;
|
|
||||||
m_exit_requested.store(true, MemoryOrder::memory_order_release);
|
|
||||||
wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopImplementation::wake()
|
|
||||||
{
|
|
||||||
ALooper_wake(m_event_loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopImplementation::post_event(Core::EventReceiver& receiver, NonnullOwnPtr<Core::Event>&& event)
|
|
||||||
{
|
|
||||||
m_thread_event_queue.post_event(receiver, move(event));
|
|
||||||
|
|
||||||
if (&m_thread_event_queue != &Core::ThreadEventQueue::current())
|
|
||||||
wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int notifier_callback(int fd, int events, void* data)
|
|
||||||
{
|
|
||||||
auto& notifier = *static_cast<Core::Notifier*>(data);
|
|
||||||
|
|
||||||
VERIFY(fd == notifier.fd());
|
|
||||||
|
|
||||||
Core::NotificationType type = Core::NotificationType::None;
|
|
||||||
if (events & ALOOPER_EVENT_INPUT)
|
|
||||||
type |= Core::NotificationType::Read;
|
|
||||||
if (events & ALOOPER_EVENT_OUTPUT)
|
|
||||||
type |= Core::NotificationType::Write;
|
|
||||||
if (events & ALOOPER_EVENT_HANGUP)
|
|
||||||
type |= Core::NotificationType::HangUp;
|
|
||||||
if (events & ALOOPER_EVENT_ERROR)
|
|
||||||
type |= Core::NotificationType::Error;
|
|
||||||
|
|
||||||
Core::NotifierActivationEvent event(notifier.fd(), type);
|
|
||||||
notifier.dispatch_event(event);
|
|
||||||
|
|
||||||
// Wake up from ALooper_pollAll, and service this event on the event queue
|
|
||||||
current_impl().wake();
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopImplementation::register_notifier(Core::Notifier& notifier)
|
|
||||||
{
|
|
||||||
auto event_flags = 0;
|
|
||||||
switch (notifier.type()) {
|
|
||||||
case Core::Notifier::Type::Read:
|
|
||||||
event_flags = ALOOPER_EVENT_INPUT;
|
|
||||||
break;
|
|
||||||
case Core::Notifier::Type::Write:
|
|
||||||
event_flags = ALOOPER_EVENT_OUTPUT;
|
|
||||||
break;
|
|
||||||
case Core::Notifier::Type::Error:
|
|
||||||
event_flags = ALOOPER_EVENT_ERROR;
|
|
||||||
break;
|
|
||||||
case Core::Notifier::Type::HangUp:
|
|
||||||
event_flags = ALOOPER_EVENT_HANGUP;
|
|
||||||
break;
|
|
||||||
case Core::Notifier::Type::None:
|
|
||||||
TODO();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ret = ALooper_addFd(m_event_loop, notifier.fd(), ALOOPER_POLL_CALLBACK, event_flags, ¬ifier_callback, ¬ifier);
|
|
||||||
VERIFY(ret == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ALooperEventLoopImplementation::unregister_notifier(Core::Notifier& notifier)
|
|
||||||
{
|
|
||||||
ALooper_removeFd(m_event_loop, notifier.fd());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <AK/Atomic.h>
|
|
||||||
#include <AK/HashMap.h>
|
|
||||||
#include <AK/NonnullOwnPtr.h>
|
|
||||||
#include <AK/WeakPtr.h>
|
|
||||||
#include <LibCore/EventLoopImplementation.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
extern "C" struct ALooper;
|
|
||||||
|
|
||||||
namespace Ladybird {
|
|
||||||
|
|
||||||
class ALooperEventLoopManager : public Core::EventLoopManager {
|
|
||||||
public:
|
|
||||||
ALooperEventLoopManager(jobject timer_service);
|
|
||||||
virtual ~ALooperEventLoopManager() override;
|
|
||||||
virtual NonnullOwnPtr<Core::EventLoopImplementation> make_implementation() override;
|
|
||||||
|
|
||||||
virtual intptr_t register_timer(Core::EventReceiver&, int milliseconds, bool should_reload, Core::TimerShouldFireWhenNotVisible) override;
|
|
||||||
virtual void unregister_timer(intptr_t timer_id) override;
|
|
||||||
|
|
||||||
virtual void register_notifier(Core::Notifier&) override;
|
|
||||||
virtual void unregister_notifier(Core::Notifier&) override;
|
|
||||||
|
|
||||||
virtual void did_post_event() override;
|
|
||||||
|
|
||||||
Function<void()> on_did_post_event;
|
|
||||||
|
|
||||||
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
|
|
||||||
virtual int register_signal(int, Function<void(int)>) override { return 0; }
|
|
||||||
virtual void unregister_signal(int) override { }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int m_pipe[2] = {};
|
|
||||||
ALooper* m_main_looper { nullptr };
|
|
||||||
jobject m_timer_service { nullptr };
|
|
||||||
jmethodID m_register_timer { nullptr };
|
|
||||||
jmethodID m_unregister_timer { nullptr };
|
|
||||||
jclass m_timer_class { nullptr };
|
|
||||||
jmethodID m_timer_constructor { nullptr };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimerData {
|
|
||||||
WeakPtr<Core::EventReceiver> receiver;
|
|
||||||
Core::TimerShouldFireWhenNotVisible visibility;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EventLoopThreadData {
|
|
||||||
static EventLoopThreadData& the();
|
|
||||||
|
|
||||||
HashMap<long, TimerData> timers;
|
|
||||||
HashTable<Core::Notifier*> notifiers;
|
|
||||||
Core::ThreadEventQueue* thread_queue = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ALooperEventLoopImplementation : public Core::EventLoopImplementation {
|
|
||||||
public:
|
|
||||||
static NonnullOwnPtr<ALooperEventLoopImplementation> create() { return adopt_own(*new ALooperEventLoopImplementation); }
|
|
||||||
|
|
||||||
virtual ~ALooperEventLoopImplementation() override;
|
|
||||||
|
|
||||||
virtual int exec() override;
|
|
||||||
virtual size_t pump(PumpMode) override;
|
|
||||||
virtual void quit(int) override;
|
|
||||||
virtual void wake() override;
|
|
||||||
virtual void post_event(Core::EventReceiver& receiver, NonnullOwnPtr<Core::Event>&&) override;
|
|
||||||
|
|
||||||
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
|
|
||||||
virtual void unquit() override { }
|
|
||||||
virtual bool was_exit_requested() const override { return false; }
|
|
||||||
virtual void notify_forked_and_in_child() override { }
|
|
||||||
|
|
||||||
EventLoopThreadData& thread_data();
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class ALooperEventLoopManager;
|
|
||||||
|
|
||||||
ALooperEventLoopImplementation();
|
|
||||||
|
|
||||||
void register_notifier(Core::Notifier&);
|
|
||||||
void unregister_notifier(Core::Notifier&);
|
|
||||||
|
|
||||||
ALooper* m_event_loop { nullptr };
|
|
||||||
int m_exit_code { 0 };
|
|
||||||
Atomic<bool> m_exit_requested { false };
|
|
||||||
EventLoopThreadData* m_thread_data { nullptr };
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
* Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ImageDecoder/ConnectionFromClient.h>
|
|
||||||
#include <LibCore/EventLoop.h>
|
|
||||||
#include <LibIPC/SingleServer.h>
|
|
||||||
|
|
||||||
ErrorOr<int> service_main(int ipc_socket)
|
|
||||||
{
|
|
||||||
Core::EventLoop event_loop;
|
|
||||||
|
|
||||||
auto socket = TRY(Core::LocalSocket::adopt_fd(ipc_socket));
|
|
||||||
auto client = TRY(ImageDecoder::ConnectionFromClient::try_create(move(socket)));
|
|
||||||
|
|
||||||
return event_loop.exec();
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JNIHelpers.h"
|
|
||||||
#include <AK/Utf16View.h>
|
|
||||||
|
|
||||||
namespace Ladybird {
|
|
||||||
jstring JavaEnvironment::jstring_from_ak_string(String const& str)
|
|
||||||
{
|
|
||||||
auto as_utf16 = MUST(AK::utf8_to_utf16(str.code_points()));
|
|
||||||
return m_env->NewString(as_utf16.data(), as_utf16.size());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <AK/Assertions.h>
|
|
||||||
#include <AK/String.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
namespace Ladybird {
|
|
||||||
class JavaEnvironment {
|
|
||||||
public:
|
|
||||||
JavaEnvironment(JavaVM* vm)
|
|
||||||
: m_vm(vm)
|
|
||||||
{
|
|
||||||
auto ret = m_vm->GetEnv(reinterpret_cast<void**>(&m_env), JNI_VERSION_1_6);
|
|
||||||
if (ret == JNI_EDETACHED) {
|
|
||||||
ret = m_vm->AttachCurrentThread(&m_env, nullptr);
|
|
||||||
VERIFY(ret == JNI_OK);
|
|
||||||
m_did_attach_thread = true;
|
|
||||||
} else if (ret == JNI_EVERSION) {
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
} else {
|
|
||||||
VERIFY(ret == JNI_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
VERIFY(m_env != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
~JavaEnvironment()
|
|
||||||
{
|
|
||||||
if (m_did_attach_thread)
|
|
||||||
m_vm->DetachCurrentThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEnv* get() const { return m_env; }
|
|
||||||
|
|
||||||
jstring jstring_from_ak_string(String const& str);
|
|
||||||
|
|
||||||
private:
|
|
||||||
JavaVM* m_vm = nullptr;
|
|
||||||
JNIEnv* m_env = nullptr;
|
|
||||||
bool m_did_attach_thread = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
extern JavaVM* global_vm;
|
|
|
@ -1,226 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ALooperEventLoopImplementation.h"
|
|
||||||
#include "JNIHelpers.h"
|
|
||||||
#include <AK/ByteString.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, ByteString output_directory);
|
|
||||||
|
|
||||||
JavaVM* global_vm;
|
|
||||||
static OwnPtr<Core::EventLoop> s_main_event_loop;
|
|
||||||
static jobject s_java_instance;
|
|
||||||
static jmethodID s_schedule_event_loop_method;
|
|
||||||
|
|
||||||
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!");
|
|
||||||
}
|
|
||||||
|
|
||||||
env->GetJavaVM(&global_vm);
|
|
||||||
VERIFY(global_vm);
|
|
||||||
|
|
||||||
s_java_instance = env->NewGlobalRef(thiz);
|
|
||||||
jclass clazz = env->GetObjectClass(s_java_instance);
|
|
||||||
VERIFY(clazz);
|
|
||||||
s_schedule_event_loop_method = env->GetMethodID(clazz, "scheduleEventLoop", "()V");
|
|
||||||
VERIFY(s_schedule_event_loop_method);
|
|
||||||
env->DeleteLocalRef(clazz);
|
|
||||||
|
|
||||||
jobject timer_service_ref = env->NewGlobalRef(timer_service);
|
|
||||||
|
|
||||||
auto* event_loop_manager = new Ladybird::ALooperEventLoopManager(timer_service_ref);
|
|
||||||
event_loop_manager->on_did_post_event = [] {
|
|
||||||
Ladybird::JavaEnvironment env(global_vm);
|
|
||||||
env.get()->CallVoidMethod(s_java_instance, s_schedule_event_loop_method);
|
|
||||||
};
|
|
||||||
Core::EventLoopManager::install(*event_loop_manager);
|
|
||||||
s_main_event_loop = make<Core::EventLoop>();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_LadybirdActivity_execMainEventLoop(JNIEnv*, jobject /* thiz */)
|
|
||||||
{
|
|
||||||
if (s_main_event_loop) {
|
|
||||||
s_main_event_loop->pump(Core::EventLoop::WaitMode::PollForEvents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_LadybirdActivity_disposeNativeCode(JNIEnv* env, jobject /* thiz */)
|
|
||||||
{
|
|
||||||
s_main_event_loop = nullptr;
|
|
||||||
s_schedule_event_loop_method = nullptr;
|
|
||||||
env->DeleteGlobalRef(s_java_instance);
|
|
||||||
|
|
||||||
delete &Core::EventLoopManager::the();
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<void> extract_tar_archive(String archive_file, ByteString output_directory)
|
|
||||||
{
|
|
||||||
constexpr size_t buffer_size = 4096;
|
|
||||||
|
|
||||||
auto file = TRY(Core::InputBufferedFile::create(TRY(Core::File::open(archive_file, Core::File::OpenMode::Read))));
|
|
||||||
|
|
||||||
ByteString 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<ByteString, ByteString> global_overrides;
|
|
||||||
HashMap<ByteString, ByteString> local_overrides;
|
|
||||||
|
|
||||||
auto get_override = [&](StringView key) -> Optional<ByteString> {
|
|
||||||
Optional<ByteString> maybe_local = local_overrides.get(key);
|
|
||||||
|
|
||||||
if (maybe_local.has_value())
|
|
||||||
return maybe_local;
|
|
||||||
|
|
||||||
Optional<ByteString> 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_byte_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());
|
|
||||||
ByteString filename = get_override("path"sv).value_or(path.string());
|
|
||||||
|
|
||||||
ByteString absolute_path = TRY(FileSystem::absolute_path(filename));
|
|
||||||
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 {};
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <AK/Error.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
ErrorOr<int> service_main(int ipc_socket);
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "LadybirdServiceBase.h"
|
|
||||||
#include <AK/Atomic.h>
|
|
||||||
#include <AK/Format.h>
|
|
||||||
#include <Ladybird/Utilities.h>
|
|
||||||
#include <LibCore/ResourceImplementationFile.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
JavaVM* global_vm;
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_LadybirdServiceBase_nativeThreadLoop(JNIEnv*, jobject /* thiz */, jint ipc_socket)
|
|
||||||
{
|
|
||||||
auto ret = service_main(ipc_socket);
|
|
||||||
if (ret.is_error()) {
|
|
||||||
warnln("Runtime Error: {}", ret.release_error());
|
|
||||||
} else {
|
|
||||||
outln("Thread exited with code {}", ret.release_value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_LadybirdServiceBase_initNativeCode(JNIEnv* env, jobject /* thiz */, jstring resource_dir, jstring tag_name)
|
|
||||||
{
|
|
||||||
static Atomic<bool> s_initialized_flag { false };
|
|
||||||
if (s_initialized_flag.exchange(true) == true) {
|
|
||||||
// Skip initializing if someone else already started the process at some point in the past
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
env->GetJavaVM(&global_vm);
|
|
||||||
|
|
||||||
char const* raw_resource_dir = env->GetStringUTFChars(resource_dir, nullptr);
|
|
||||||
s_serenity_resource_root = raw_resource_dir;
|
|
||||||
env->ReleaseStringUTFChars(resource_dir, raw_resource_dir);
|
|
||||||
// FIXME: Use a custom Android version that uses AssetManager to load files.
|
|
||||||
Core::ResourceImplementation::install(make<Core::ResourceImplementationFile>(MUST(String::formatted("{}/res", s_serenity_resource_root))));
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <AK/LexicalPath.h>
|
|
||||||
#include <AK/OwnPtr.h>
|
|
||||||
#include <Ladybird/Utilities.h>
|
|
||||||
#include <LibCore/ArgsParser.h>
|
|
||||||
#include <LibCore/EventLoop.h>
|
|
||||||
#include <LibCore/LocalServer.h>
|
|
||||||
#include <LibCore/System.h>
|
|
||||||
#include <LibFileSystem/FileSystem.h>
|
|
||||||
#include <LibIPC/SingleServer.h>
|
|
||||||
#include <LibTLS/Certificate.h>
|
|
||||||
#include <RequestServer/ConnectionFromClient.h>
|
|
||||||
#include <RequestServer/GeminiProtocol.h>
|
|
||||||
#include <RequestServer/HttpProtocol.h>
|
|
||||||
#include <RequestServer/HttpsProtocol.h>
|
|
||||||
|
|
||||||
// FIXME: Share b/w RequestServer and WebSocket
|
|
||||||
ErrorOr<ByteString> find_certificates(StringView serenity_resource_root)
|
|
||||||
{
|
|
||||||
auto cert_path = ByteString::formatted("{}/res/ladybird/cacert.pem", serenity_resource_root);
|
|
||||||
if (!FileSystem::exists(cert_path))
|
|
||||||
return Error::from_string_view("Don't know how to load certs!"sv);
|
|
||||||
return cert_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<int> service_main(int ipc_socket)
|
|
||||||
{
|
|
||||||
// Ensure the certificates are read out here.
|
|
||||||
DefaultRootCACertificates::set_default_certificate_paths(Vector { TRY(find_certificates(s_serenity_resource_root)) });
|
|
||||||
[[maybe_unused]] auto& certs = DefaultRootCACertificates::the();
|
|
||||||
|
|
||||||
Core::EventLoop event_loop;
|
|
||||||
|
|
||||||
RequestServer::GeminiProtocol::install();
|
|
||||||
RequestServer::HttpProtocol::install();
|
|
||||||
RequestServer::HttpsProtocol::install();
|
|
||||||
|
|
||||||
auto socket = TRY(Core::LocalSocket::adopt_fd(ipc_socket));
|
|
||||||
auto client = TRY(RequestServer::ConnectionFromClient::try_create(move(socket)));
|
|
||||||
|
|
||||||
return event_loop.exec();
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ALooperEventLoopImplementation.h"
|
|
||||||
#include <LibCore/EventLoop.h>
|
|
||||||
#include <LibCore/ThreadEventQueue.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_TimerExecutorService_00024Timer_nativeRun(JNIEnv*, jobject /* thiz */, jlong native_data, jlong id)
|
|
||||||
{
|
|
||||||
static Core::EventLoop s_event_loop; // Here to exist for this thread
|
|
||||||
|
|
||||||
auto& event_loop_impl = *reinterpret_cast<Ladybird::ALooperEventLoopImplementation*>(native_data);
|
|
||||||
auto& thread_data = event_loop_impl.thread_data();
|
|
||||||
|
|
||||||
if (auto timer_data = thread_data.timers.get(id); timer_data.has_value()) {
|
|
||||||
auto receiver = timer_data->receiver.strong_ref();
|
|
||||||
if (!receiver)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (timer_data->visibility == Core::TimerShouldFireWhenNotVisible::No)
|
|
||||||
if (!receiver->is_visible_for_timer_purposes())
|
|
||||||
return;
|
|
||||||
|
|
||||||
event_loop_impl.post_event(*receiver, make<Core::TimerEvent>());
|
|
||||||
}
|
|
||||||
// Flush the event loop on this thread to keep any garbage from building up
|
|
||||||
if (auto num_events = s_event_loop.pump(Core::EventLoop::WaitMode::PollForEvents); num_events != 0) {
|
|
||||||
dbgln("BUG: Processed {} events on Timer thread!", num_events);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,159 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "WebContentService.h"
|
|
||||||
#include "LadybirdServiceBase.h"
|
|
||||||
#include <AK/LexicalPath.h>
|
|
||||||
#include <Ladybird/FontPlugin.h>
|
|
||||||
#include <Ladybird/HelperProcess.h>
|
|
||||||
#include <Ladybird/ImageCodecPlugin.h>
|
|
||||||
#include <Ladybird/Utilities.h>
|
|
||||||
#include <LibAudio/Loader.h>
|
|
||||||
#include <LibCore/ArgsParser.h>
|
|
||||||
#include <LibCore/EventLoop.h>
|
|
||||||
#include <LibCore/LocalServer.h>
|
|
||||||
#include <LibCore/System.h>
|
|
||||||
#include <LibIPC/ConnectionFromClient.h>
|
|
||||||
#include <LibImageDecoderClient/Client.h>
|
|
||||||
#include <LibJS/Bytecode/Interpreter.h>
|
|
||||||
#include <LibProtocol/RequestClient.h>
|
|
||||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
|
||||||
#include <LibWeb/HTML/Window.h>
|
|
||||||
#include <LibWeb/Loader/ContentFilter.h>
|
|
||||||
#include <LibWeb/Loader/GeneratedPagesLoader.h>
|
|
||||||
#include <LibWeb/Loader/ResourceLoader.h>
|
|
||||||
#include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h>
|
|
||||||
#include <LibWeb/Platform/AudioCodecPluginAgnostic.h>
|
|
||||||
#include <LibWeb/Platform/EventLoopPluginSerenity.h>
|
|
||||||
#include <LibWebView/RequestServerAdapter.h>
|
|
||||||
#include <WebContent/ConnectionFromClient.h>
|
|
||||||
#include <WebContent/PageHost.h>
|
|
||||||
|
|
||||||
static ErrorOr<NonnullRefPtr<Protocol::RequestClient>> bind_request_server_service()
|
|
||||||
{
|
|
||||||
return bind_service<Protocol::RequestClient>(&bind_request_server_java);
|
|
||||||
}
|
|
||||||
|
|
||||||
template ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>, Error>
|
|
||||||
bind_service<ImageDecoderClient::Client>(void (*)(int));
|
|
||||||
|
|
||||||
static ErrorOr<void> load_content_filters();
|
|
||||||
|
|
||||||
static ErrorOr<void> load_autoplay_allowlist();
|
|
||||||
|
|
||||||
ErrorOr<int> service_main(int ipc_socket)
|
|
||||||
{
|
|
||||||
Core::EventLoop event_loop;
|
|
||||||
|
|
||||||
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity);
|
|
||||||
Web::Platform::ImageCodecPlugin::install(*new Ladybird::ImageCodecPlugin);
|
|
||||||
|
|
||||||
Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) {
|
|
||||||
(void)loader;
|
|
||||||
return Error::from_string_literal("Don't know how to initialize audio in this configuration!");
|
|
||||||
});
|
|
||||||
|
|
||||||
auto request_server_client = TRY(bind_request_server_service());
|
|
||||||
Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create(move(request_server_client))));
|
|
||||||
|
|
||||||
bool is_layout_test_mode = false;
|
|
||||||
|
|
||||||
Web::HTML::Window::set_internals_object_exposed(is_layout_test_mode);
|
|
||||||
Web::Platform::FontPlugin::install(*new Ladybird::FontPlugin(is_layout_test_mode));
|
|
||||||
|
|
||||||
TRY(Web::Bindings::initialize_main_thread_vm());
|
|
||||||
|
|
||||||
auto maybe_content_filter_error = load_content_filters();
|
|
||||||
if (maybe_content_filter_error.is_error())
|
|
||||||
dbgln("Failed to load content filters: {}", maybe_content_filter_error.error());
|
|
||||||
|
|
||||||
auto maybe_autoplay_allowlist_error = load_autoplay_allowlist();
|
|
||||||
if (maybe_autoplay_allowlist_error.is_error())
|
|
||||||
dbgln("Failed to load autoplay allowlist: {}", maybe_autoplay_allowlist_error.error());
|
|
||||||
|
|
||||||
auto webcontent_socket = TRY(Core::LocalSocket::adopt_fd(ipc_socket));
|
|
||||||
auto webcontent_client = TRY(WebContent::ConnectionFromClient::try_create(move(webcontent_socket)));
|
|
||||||
|
|
||||||
return event_loop.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Client>
|
|
||||||
ErrorOr<NonnullRefPtr<Client>> bind_service(void (*bind_method)(int))
|
|
||||||
{
|
|
||||||
int socket_fds[2] {};
|
|
||||||
TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
|
|
||||||
|
|
||||||
int ui_fd = socket_fds[0];
|
|
||||||
int server_fd = socket_fds[1];
|
|
||||||
|
|
||||||
// NOTE: The java object takes ownership of the socket fds
|
|
||||||
(*bind_method)(server_fd);
|
|
||||||
|
|
||||||
auto socket = TRY(Core::LocalSocket::adopt_fd(ui_fd));
|
|
||||||
TRY(socket->set_blocking(true));
|
|
||||||
|
|
||||||
auto new_client = TRY(try_make_ref_counted<Client>(move(socket)));
|
|
||||||
|
|
||||||
return new_client;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ErrorOr<void> load_content_filters()
|
|
||||||
{
|
|
||||||
auto file_or_error = Core::File::open(ByteString::formatted("{}/home/anon/.config/BrowserContentFilters.txt", s_serenity_resource_root), Core::File::OpenMode::Read);
|
|
||||||
if (file_or_error.is_error())
|
|
||||||
file_or_error = Core::File::open(ByteString::formatted("{}/res/ladybird/BrowserContentFilters.txt", s_serenity_resource_root), Core::File::OpenMode::Read);
|
|
||||||
if (file_or_error.is_error())
|
|
||||||
return file_or_error.release_error();
|
|
||||||
|
|
||||||
auto file = file_or_error.release_value();
|
|
||||||
auto ad_filter_list = TRY(Core::InputBufferedFile::create(move(file)));
|
|
||||||
auto buffer = TRY(ByteBuffer::create_uninitialized(4096));
|
|
||||||
|
|
||||||
Vector<String> patterns;
|
|
||||||
|
|
||||||
while (TRY(ad_filter_list->can_read_line())) {
|
|
||||||
auto line = TRY(ad_filter_list->read_line(buffer));
|
|
||||||
if (line.is_empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto pattern = TRY(String::from_utf8(line));
|
|
||||||
TRY(patterns.try_append(move(pattern)));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& content_filter = Web::ContentFilter::the();
|
|
||||||
TRY(content_filter.set_patterns(patterns));
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static ErrorOr<void> load_autoplay_allowlist()
|
|
||||||
{
|
|
||||||
auto file_or_error = Core::File::open(TRY(String::formatted("{}/home/anon/.config/BrowserAutoplayAllowlist.txt", s_serenity_resource_root)), Core::File::OpenMode::Read);
|
|
||||||
if (file_or_error.is_error())
|
|
||||||
file_or_error = Core::File::open(TRY(String::formatted("{}/res/ladybird/BrowserAutoplayAllowlist.txt", s_serenity_resource_root)), Core::File::OpenMode::Read);
|
|
||||||
if (file_or_error.is_error())
|
|
||||||
return file_or_error.release_error();
|
|
||||||
|
|
||||||
auto file = file_or_error.release_value();
|
|
||||||
auto allowlist = TRY(Core::InputBufferedFile::create(move(file)));
|
|
||||||
auto buffer = TRY(ByteBuffer::create_uninitialized(4096));
|
|
||||||
|
|
||||||
Vector<String> origins;
|
|
||||||
|
|
||||||
while (TRY(allowlist->can_read_line())) {
|
|
||||||
auto line = TRY(allowlist->read_line(buffer));
|
|
||||||
if (line.is_empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto domain = TRY(String::from_utf8(line));
|
|
||||||
TRY(origins.try_append(move(domain)));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& autoplay_allowlist = Web::PermissionsPolicy::AutoplayAllowlist::the();
|
|
||||||
TRY(autoplay_allowlist.enable_for_origins(origins));
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <AK/NonnullRefPtr.h>
|
|
||||||
|
|
||||||
template<typename Client>
|
|
||||||
ErrorOr<NonnullRefPtr<Client>> bind_service(void (*bind_method)(int));
|
|
||||||
|
|
||||||
void bind_request_server_java(int ipc_socket);
|
|
||||||
void bind_image_decoder_java(int ipc_socket);
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JNIHelpers.h"
|
|
||||||
#include "LadybirdServiceBase.h"
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
jobject global_instance;
|
|
||||||
jclass global_class_reference;
|
|
||||||
jmethodID bind_request_server_method;
|
|
||||||
jmethodID bind_image_decoder_method;
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_WebContentService_nativeInit(JNIEnv* env, jobject thiz)
|
|
||||||
{
|
|
||||||
global_instance = env->NewGlobalRef(thiz);
|
|
||||||
|
|
||||||
auto local_class = env->FindClass("org/serenityos/ladybird/WebContentService");
|
|
||||||
if (!local_class)
|
|
||||||
TODO();
|
|
||||||
global_class_reference = reinterpret_cast<jclass>(env->NewGlobalRef(local_class));
|
|
||||||
env->DeleteLocalRef(local_class);
|
|
||||||
|
|
||||||
auto method = env->GetMethodID(global_class_reference, "bindRequestServer", "(I)V");
|
|
||||||
if (!method)
|
|
||||||
TODO();
|
|
||||||
bind_request_server_method = method;
|
|
||||||
|
|
||||||
method = env->GetMethodID(global_class_reference, "bindImageDecoder", "(I)V");
|
|
||||||
if (!method)
|
|
||||||
TODO();
|
|
||||||
bind_image_decoder_method = method;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bind_request_server_java(int ipc_socket)
|
|
||||||
{
|
|
||||||
Ladybird::JavaEnvironment env(global_vm);
|
|
||||||
env.get()->CallVoidMethod(global_instance, bind_request_server_method, ipc_socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bind_image_decoder_java(int ipc_socket)
|
|
||||||
{
|
|
||||||
Ladybird::JavaEnvironment env(global_vm);
|
|
||||||
env.get()->CallVoidMethod(global_instance, bind_image_decoder_method, ipc_socket);
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "WebViewImplementationNative.h"
|
|
||||||
#include "JNIHelpers.h"
|
|
||||||
#include <LibWebView/WebContentClient.h>
|
|
||||||
#include <Userland/Libraries/LibGfx/Bitmap.h>
|
|
||||||
#include <Userland/Libraries/LibGfx/Painter.h>
|
|
||||||
#include <Userland/Libraries/LibWeb/Crypto/Crypto.h>
|
|
||||||
#include <Userland/Libraries/LibWebView/ViewImplementation.h>
|
|
||||||
#include <android/bitmap.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
namespace Ladybird {
|
|
||||||
static Gfx::BitmapFormat to_gfx_bitmap_format(i32 f)
|
|
||||||
{
|
|
||||||
switch (f) {
|
|
||||||
case ANDROID_BITMAP_FORMAT_RGBA_8888:
|
|
||||||
return Gfx::BitmapFormat::BGRA8888;
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WebViewImplementationNative::WebViewImplementationNative(jobject thiz)
|
|
||||||
: m_java_instance(thiz)
|
|
||||||
{
|
|
||||||
// NOTE: m_java_instance's global ref is controlled by the JNI bindings
|
|
||||||
initialize_client(CreateNewClient::Yes);
|
|
||||||
|
|
||||||
on_ready_to_paint = [this]() {
|
|
||||||
JavaEnvironment env(global_vm);
|
|
||||||
env.get()->CallVoidMethod(m_java_instance, invalidate_layout_method);
|
|
||||||
};
|
|
||||||
|
|
||||||
on_load_start = [this](URL::URL const& url, bool is_redirect) {
|
|
||||||
JavaEnvironment env(global_vm);
|
|
||||||
auto url_string = env.jstring_from_ak_string(MUST(url.to_string()));
|
|
||||||
env.get()->CallVoidMethod(m_java_instance, on_load_start_method, url_string, is_redirect);
|
|
||||||
env.get()->DeleteLocalRef(url_string);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebViewImplementationNative::initialize_client(WebView::ViewImplementation::CreateNewClient)
|
|
||||||
{
|
|
||||||
m_client_state = {};
|
|
||||||
|
|
||||||
auto new_client = bind_web_content_client();
|
|
||||||
|
|
||||||
m_client_state.client = new_client;
|
|
||||||
m_client_state.client->on_web_content_process_crash = [] {
|
|
||||||
warnln("WebContent crashed!");
|
|
||||||
// FIXME: launch a new client
|
|
||||||
};
|
|
||||||
|
|
||||||
m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid());
|
|
||||||
client().async_set_window_handle(0, m_client_state.client_handle);
|
|
||||||
|
|
||||||
client().async_set_device_pixels_per_css_pixel(0, m_device_pixel_ratio);
|
|
||||||
|
|
||||||
// FIXME: update_palette, update system fonts
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebViewImplementationNative::paint_into_bitmap(void* android_bitmap_raw, AndroidBitmapInfo const& info)
|
|
||||||
{
|
|
||||||
// Software bitmaps only for now!
|
|
||||||
VERIFY((info.flags & ANDROID_BITMAP_FLAGS_IS_HARDWARE) == 0);
|
|
||||||
|
|
||||||
auto android_bitmap = MUST(Gfx::Bitmap::create_wrapper(to_gfx_bitmap_format(info.format), { info.width, info.height }, 1, info.stride, android_bitmap_raw));
|
|
||||||
Gfx::Painter painter(android_bitmap);
|
|
||||||
if (auto* bitmap = m_client_state.has_usable_bitmap ? m_client_state.front_bitmap.bitmap.ptr() : m_backup_bitmap.ptr())
|
|
||||||
painter.blit({ 0, 0 }, *bitmap, bitmap->rect());
|
|
||||||
else
|
|
||||||
painter.clear_rect(painter.clip_rect(), Gfx::Color::Magenta);
|
|
||||||
|
|
||||||
// Convert our internal BGRA into RGBA. This will be slowwwwwww
|
|
||||||
// FIXME: Don't do a color format swap here.
|
|
||||||
for (auto y = 0; y < android_bitmap->height(); ++y) {
|
|
||||||
auto* scanline = android_bitmap->scanline(y);
|
|
||||||
for (auto x = 0; x < android_bitmap->width(); ++x) {
|
|
||||||
auto current_pixel = scanline[x];
|
|
||||||
u32 alpha = (current_pixel & 0xFF000000U) >> 24;
|
|
||||||
u32 red = (current_pixel & 0x00FF0000U) >> 16;
|
|
||||||
u32 green = (current_pixel & 0x0000FF00U) >> 8;
|
|
||||||
u32 blue = (current_pixel & 0x000000FFU);
|
|
||||||
scanline[x] = (alpha << 24U) | (blue << 16U) | (green << 8U) | red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebViewImplementationNative::set_viewport_geometry(int w, int h)
|
|
||||||
{
|
|
||||||
m_viewport_rect = { { 0, 0 }, { w, h } };
|
|
||||||
client().async_set_viewport_rect(0, m_viewport_rect);
|
|
||||||
handle_resize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebViewImplementationNative::set_device_pixel_ratio(float f)
|
|
||||||
{
|
|
||||||
m_device_pixel_ratio = f;
|
|
||||||
client().async_set_device_pixels_per_css_pixel(0, m_device_pixel_ratio);
|
|
||||||
}
|
|
||||||
|
|
||||||
NonnullRefPtr<WebView::WebContentClient> WebViewImplementationNative::bind_web_content_client()
|
|
||||||
{
|
|
||||||
JavaEnvironment env(global_vm);
|
|
||||||
|
|
||||||
int socket_fds[2] {};
|
|
||||||
MUST(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
|
|
||||||
|
|
||||||
int ui_fd = socket_fds[0];
|
|
||||||
int wc_fd = socket_fds[1];
|
|
||||||
|
|
||||||
// NOTE: The java object takes ownership of the socket fds
|
|
||||||
env.get()->CallVoidMethod(m_java_instance, bind_webcontent_method, wc_fd);
|
|
||||||
|
|
||||||
auto socket = MUST(Core::LocalSocket::adopt_fd(ui_fd));
|
|
||||||
MUST(socket->set_blocking(true));
|
|
||||||
|
|
||||||
auto new_client = make_ref_counted<WebView::WebContentClient>(move(socket), *this);
|
|
||||||
|
|
||||||
return new_client;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Userland/Libraries/LibWebView/ViewImplementation.h>
|
|
||||||
#include <android/bitmap.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
namespace Ladybird {
|
|
||||||
class WebViewImplementationNative : public WebView::ViewImplementation {
|
|
||||||
public:
|
|
||||||
WebViewImplementationNative(jobject thiz);
|
|
||||||
|
|
||||||
virtual Web::DevicePixelRect viewport_rect() const override { return m_viewport_rect; }
|
|
||||||
virtual Gfx::IntPoint to_content_position(Gfx::IntPoint p) const override { return p; }
|
|
||||||
virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint p) const override { return p; }
|
|
||||||
virtual void update_zoom() override { }
|
|
||||||
|
|
||||||
NonnullRefPtr<WebView::WebContentClient> bind_web_content_client();
|
|
||||||
|
|
||||||
virtual void initialize_client(CreateNewClient) override;
|
|
||||||
|
|
||||||
void paint_into_bitmap(void* android_bitmap_raw, AndroidBitmapInfo const& info);
|
|
||||||
|
|
||||||
void set_viewport_geometry(int w, int h);
|
|
||||||
void set_device_pixel_ratio(float f);
|
|
||||||
|
|
||||||
static jclass global_class_reference;
|
|
||||||
static jmethodID bind_webcontent_method;
|
|
||||||
static jmethodID invalidate_layout_method;
|
|
||||||
static jmethodID on_load_start_method;
|
|
||||||
|
|
||||||
jobject java_instance() const { return m_java_instance; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
jobject m_java_instance = nullptr;
|
|
||||||
Web::DevicePixelRect m_viewport_rect;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "WebViewImplementationNative.h"
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
using namespace Ladybird;
|
|
||||||
|
|
||||||
jclass WebViewImplementationNative::global_class_reference;
|
|
||||||
jmethodID WebViewImplementationNative::bind_webcontent_method;
|
|
||||||
jmethodID WebViewImplementationNative::invalidate_layout_method;
|
|
||||||
jmethodID WebViewImplementationNative::on_load_start_method;
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_WebViewImplementation_00024Companion_nativeClassInit(JNIEnv* env, jobject /* thiz */)
|
|
||||||
{
|
|
||||||
auto local_class = env->FindClass("org/serenityos/ladybird/WebViewImplementation");
|
|
||||||
if (!local_class)
|
|
||||||
TODO();
|
|
||||||
WebViewImplementationNative::global_class_reference = reinterpret_cast<jclass>(env->NewGlobalRef(local_class));
|
|
||||||
env->DeleteLocalRef(local_class);
|
|
||||||
|
|
||||||
auto method = env->GetMethodID(WebViewImplementationNative::global_class_reference, "bindWebContentService", "(I)V");
|
|
||||||
if (!method)
|
|
||||||
TODO();
|
|
||||||
WebViewImplementationNative::bind_webcontent_method = method;
|
|
||||||
|
|
||||||
method = env->GetMethodID(WebViewImplementationNative::global_class_reference, "invalidateLayout", "()V");
|
|
||||||
if (!method)
|
|
||||||
TODO();
|
|
||||||
WebViewImplementationNative::invalidate_layout_method = method;
|
|
||||||
|
|
||||||
method = env->GetMethodID(WebViewImplementationNative::global_class_reference, "onLoadStart", "(Ljava/lang/String;Z)V");
|
|
||||||
if (!method)
|
|
||||||
TODO();
|
|
||||||
WebViewImplementationNative::on_load_start_method = method;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT jlong JNICALL
|
|
||||||
Java_org_serenityos_ladybird_WebViewImplementation_nativeObjectInit(JNIEnv* env, jobject thiz)
|
|
||||||
{
|
|
||||||
auto ref = env->NewGlobalRef(thiz);
|
|
||||||
auto instance = reinterpret_cast<jlong>(new WebViewImplementationNative(ref));
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_WebViewImplementation_nativeObjectDispose(JNIEnv* env, jobject /* thiz */, jlong instance)
|
|
||||||
{
|
|
||||||
auto* impl = reinterpret_cast<WebViewImplementationNative*>(instance);
|
|
||||||
env->DeleteGlobalRef(impl->java_instance());
|
|
||||||
delete impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_WebViewImplementation_nativeDrawIntoBitmap(JNIEnv* env, jobject /* thiz */, jlong instance, jobject bitmap)
|
|
||||||
{
|
|
||||||
auto* impl = reinterpret_cast<WebViewImplementationNative*>(instance);
|
|
||||||
|
|
||||||
AndroidBitmapInfo bitmap_info = {};
|
|
||||||
void* pixels = nullptr;
|
|
||||||
AndroidBitmap_getInfo(env, bitmap, &bitmap_info);
|
|
||||||
AndroidBitmap_lockPixels(env, bitmap, &pixels);
|
|
||||||
if (pixels)
|
|
||||||
impl->paint_into_bitmap(pixels, bitmap_info);
|
|
||||||
|
|
||||||
AndroidBitmap_unlockPixels(env, bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_WebViewImplementation_nativeSetViewportGeometry(JNIEnv*, jobject /* thiz */, jlong instance, jint w, jint h)
|
|
||||||
{
|
|
||||||
auto* impl = reinterpret_cast<WebViewImplementationNative*>(instance);
|
|
||||||
impl->set_viewport_geometry(w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_WebViewImplementation_nativeLoadURL(JNIEnv* env, jobject /* thiz */, jlong instance, jstring url)
|
|
||||||
{
|
|
||||||
auto* impl = reinterpret_cast<WebViewImplementationNative*>(instance);
|
|
||||||
char const* raw_url = env->GetStringUTFChars(url, nullptr);
|
|
||||||
auto ak_url = URL::create_with_url_or_path(StringView { raw_url, strlen(raw_url) });
|
|
||||||
env->ReleaseStringUTFChars(url, raw_url);
|
|
||||||
impl->load(ak_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
|
||||||
Java_org_serenityos_ladybird_WebViewImplementation_nativeSetDevicePixelRatio(JNIEnv*, jobject /* thiz */, jlong instance, jfloat ratio)
|
|
||||||
{
|
|
||||||
auto* impl = reinterpret_cast<WebViewImplementationNative*>(instance);
|
|
||||||
impl->set_device_pixel_ratio(ratio);
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import android.os.Message
|
|
||||||
|
|
||||||
class ImageDecoderService : LadybirdServiceBase("ImageDecoderService") {
|
|
||||||
override fun handleServiceSpecificMessage(msg: Message): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
init {
|
|
||||||
System.loadLibrary("imagedecoder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.KeyEvent
|
|
||||||
import android.view.inputmethod.EditorInfo
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.TextView
|
|
||||||
import org.serenityos.ladybird.databinding.ActivityMainBinding
|
|
||||||
|
|
||||||
class LadybirdActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
|
||||||
private lateinit var resourceDir: String
|
|
||||||
private lateinit var view: WebView
|
|
||||||
private lateinit var urlEditText: EditText
|
|
||||||
private var timerService = TimerExecutorService()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
resourceDir = TransferAssets.transferAssets(this)
|
|
||||||
initNativeCode(resourceDir, "Ladybird", timerService)
|
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
urlEditText = binding.urlEditText
|
|
||||||
view = binding.webView
|
|
||||||
view.onLoadStart = { url: String, _ ->
|
|
||||||
urlEditText.setText(url, TextView.BufferType.EDITABLE)
|
|
||||||
}
|
|
||||||
urlEditText.setOnEditorActionListener { textView: TextView, actionId: Int, _: KeyEvent? ->
|
|
||||||
when (actionId) {
|
|
||||||
EditorInfo.IME_ACTION_GO, EditorInfo.IME_ACTION_SEARCH -> view.loadURL(textView.text.toString())
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
view.initialize(resourceDir)
|
|
||||||
view.loadURL(intent.dataString ?: "https://ladybird.dev")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
view.dispose()
|
|
||||||
disposeNativeCode()
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun scheduleEventLoop() {
|
|
||||||
mainExecutor.execute {
|
|
||||||
execMainEventLoop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private external fun initNativeCode(
|
|
||||||
resourceDir: String, tag: String, timerService: TimerExecutorService
|
|
||||||
)
|
|
||||||
|
|
||||||
private external fun disposeNativeCode()
|
|
||||||
private external fun execMainEventLoop()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
// Used to load the 'ladybird' library on application startup.
|
|
||||||
init {
|
|
||||||
System.loadLibrary("Ladybird")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
|
||||||
import android.util.Log
|
|
||||||
import android.os.ParcelFileDescriptor
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.os.Looper
|
|
||||||
import android.os.Message
|
|
||||||
import android.os.Messenger
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
const val MSG_SET_RESOURCE_ROOT = 1
|
|
||||||
const val MSG_TRANSFER_SOCKET = 2
|
|
||||||
|
|
||||||
abstract class LadybirdServiceBase(protected val TAG: String) : Service() {
|
|
||||||
private val threadPool = Executors.newCachedThreadPool()
|
|
||||||
protected lateinit var resourceDir: String
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
Log.i(TAG, "Creating Service")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
Log.i(TAG, "Destroying Service")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
||||||
Log.i(TAG, "Start command received")
|
|
||||||
return super.onStartCommand(intent, flags, startId)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleTransferSockets(msg: Message) {
|
|
||||||
val bundle = msg.data
|
|
||||||
// FIXME: Handle garbage messages from wierd clients
|
|
||||||
val ipcSocket = bundle.getParcelable<ParcelFileDescriptor>("IPC_SOCKET")!!
|
|
||||||
createThread(ipcSocket)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleSetResourceRoot(msg: Message) {
|
|
||||||
// FIXME: Handle this being already set, not being present, etc
|
|
||||||
resourceDir = msg.data.getString("PATH")!!
|
|
||||||
|
|
||||||
initNativeCode(resourceDir, TAG)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(p0: Intent?): IBinder? {
|
|
||||||
// FIXME: Check the intent to make sure it's legit
|
|
||||||
return Messenger(IncomingHandler(WeakReference(this))).binder
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun createThread(ipcSocket: ParcelFileDescriptor) {
|
|
||||||
threadPool.execute {
|
|
||||||
nativeThreadLoop(ipcSocket.detachFd())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private external fun nativeThreadLoop(ipcSocket: Int)
|
|
||||||
private external fun initNativeCode(resourceDir: String, tagName: String);
|
|
||||||
|
|
||||||
abstract fun handleServiceSpecificMessage(msg: Message): Boolean
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
class IncomingHandler(private val service: WeakReference<LadybirdServiceBase>) :
|
|
||||||
Handler(Looper.getMainLooper()) {
|
|
||||||
override fun handleMessage(msg: Message) {
|
|
||||||
when (msg.what) {
|
|
||||||
MSG_TRANSFER_SOCKET -> service.get()?.handleTransferSockets(msg)
|
|
||||||
?: super.handleMessage(msg)
|
|
||||||
|
|
||||||
MSG_SET_RESOURCE_ROOT -> service.get()?.handleSetResourceRoot(msg)
|
|
||||||
?: super.handleMessage(msg)
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
val ret = service.get()?.handleServiceSpecificMessage(msg)
|
|
||||||
if (ret == null || !ret)
|
|
||||||
super.handleMessage(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.ServiceConnection
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.os.Message
|
|
||||||
import android.os.Messenger
|
|
||||||
import android.os.ParcelFileDescriptor
|
|
||||||
|
|
||||||
class LadybirdServiceConnection(
|
|
||||||
private var ipcFd: Int,
|
|
||||||
private var resourceDir: String
|
|
||||||
) :
|
|
||||||
ServiceConnection {
|
|
||||||
var boundToService: Boolean = false
|
|
||||||
var onDisconnect: () -> Unit = {}
|
|
||||||
private var service: Messenger? = null
|
|
||||||
|
|
||||||
override fun onServiceConnected(className: ComponentName, svc: IBinder) {
|
|
||||||
// This is called when the connection with the service has been
|
|
||||||
// established, giving us the object we can use to
|
|
||||||
// interact with the service. We are communicating with the
|
|
||||||
// service using a Messenger, so here we get a client-side
|
|
||||||
// representation of that from the raw IBinder object.
|
|
||||||
service = Messenger(svc)
|
|
||||||
boundToService = true
|
|
||||||
|
|
||||||
val init = Message.obtain(null, MSG_SET_RESOURCE_ROOT)
|
|
||||||
init.data.putString("PATH", resourceDir)
|
|
||||||
service!!.send(init)
|
|
||||||
|
|
||||||
val msg = Message.obtain(null, MSG_TRANSFER_SOCKET)
|
|
||||||
msg.data.putParcelable("IPC_SOCKET", ParcelFileDescriptor.adoptFd(ipcFd))
|
|
||||||
service!!.send(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceDisconnected(className: ComponentName) {
|
|
||||||
// This is called when the connection with the service has been
|
|
||||||
// unexpectedly disconnected; that is, its process crashed.
|
|
||||||
service = null
|
|
||||||
boundToService = false
|
|
||||||
|
|
||||||
// Notify owner that the service is dead
|
|
||||||
onDisconnect()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import android.os.Message
|
|
||||||
|
|
||||||
class RequestServerService : LadybirdServiceBase("RequestServerService") {
|
|
||||||
override fun handleServiceSpecificMessage(msg: Message): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
init {
|
|
||||||
System.loadLibrary("requestserver")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import java.util.concurrent.ScheduledFuture
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class TimerExecutorService {
|
|
||||||
|
|
||||||
private val executor = Executors.newSingleThreadScheduledExecutor()
|
|
||||||
|
|
||||||
class Timer(private var nativeData: Long) : Runnable {
|
|
||||||
override fun run() {
|
|
||||||
nativeRun(nativeData, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private external fun nativeRun(nativeData: Long, id: Long)
|
|
||||||
var id: Long = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fun registerTimer(timer: Timer, singleShot: Boolean, milliseconds: Long): Long {
|
|
||||||
val id = ++nextId
|
|
||||||
timer.id = id
|
|
||||||
val handle: ScheduledFuture<*> = if (singleShot) executor.schedule(
|
|
||||||
timer,
|
|
||||||
milliseconds,
|
|
||||||
TimeUnit.MILLISECONDS
|
|
||||||
) else executor.scheduleWithFixedDelay(
|
|
||||||
timer,
|
|
||||||
milliseconds,
|
|
||||||
milliseconds,
|
|
||||||
TimeUnit.MILLISECONDS
|
|
||||||
)
|
|
||||||
timers[id] = handle
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unregisterTimer(id: Long) {
|
|
||||||
val timer = timers[id] ?: return
|
|
||||||
timer.cancel(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var nextId: Long = 0
|
|
||||||
private val timers: HashMap<Long, ScheduledFuture<*>> = hashMapOf()
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
* <p>
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import java.lang.String;
|
|
||||||
|
|
||||||
public class TransferAssets {
|
|
||||||
/**
|
|
||||||
* @return new ladybird resource root
|
|
||||||
*/
|
|
||||||
static public String transferAssets(Context context) {
|
|
||||||
Log.d("Ladybird", "Hello from java");
|
|
||||||
Context applicationContext = context.getApplicationContext();
|
|
||||||
File assetDir = applicationContext.getFilesDir();
|
|
||||||
AssetManager assetManager = applicationContext.getAssets();
|
|
||||||
if (!copyAsset(assetManager, "ladybird-assets.tar", assetDir.getAbsolutePath() + "/ladybird-assets.tar")) {
|
|
||||||
Log.e("Ladybird", "Unable to copy assets");
|
|
||||||
return "Invalid Assets, this won't work";
|
|
||||||
}
|
|
||||||
Log.d("Ladybird", "Copied ladybird-assets.tar to app-specific storage path");
|
|
||||||
return assetDir.getAbsolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ty to https://stackoverflow.com/a/22903693 for the sauce
|
|
||||||
private static boolean copyAsset(AssetManager assetManager,
|
|
||||||
String fromAssetPath, String toPath) {
|
|
||||||
try {
|
|
||||||
InputStream in = assetManager.open(fromAssetPath);
|
|
||||||
new File(toPath).createNewFile();
|
|
||||||
OutputStream out = new FileOutputStream(toPath);
|
|
||||||
copyFile(in, out);
|
|
||||||
in.close();
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void copyFile(InputStream in, OutputStream out) throws IOException {
|
|
||||||
byte[] buffer = new byte[4096];
|
|
||||||
int read;
|
|
||||||
while ((read = in.read(buffer)) != -1) {
|
|
||||||
out.write(buffer, 0, read);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Message
|
|
||||||
import android.util.Log
|
|
||||||
|
|
||||||
class WebContentService : LadybirdServiceBase("WebContentService") {
|
|
||||||
override fun handleServiceSpecificMessage(msg: Message): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
nativeInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindRequestServer(ipcFd: Int)
|
|
||||||
{
|
|
||||||
val connector = LadybirdServiceConnection(ipcFd, resourceDir)
|
|
||||||
connector.onDisconnect = {
|
|
||||||
// FIXME: Notify impl that service is dead and might need restarted
|
|
||||||
Log.e(TAG, "RequestServer Died! :(")
|
|
||||||
}
|
|
||||||
// FIXME: Unbind this at some point maybe
|
|
||||||
bindService(
|
|
||||||
Intent(this, RequestServerService::class.java),
|
|
||||||
connector,
|
|
||||||
Context.BIND_AUTO_CREATE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindImageDecoder(ipcFd: Int)
|
|
||||||
{
|
|
||||||
val connector = LadybirdServiceConnection(ipcFd, resourceDir)
|
|
||||||
connector.onDisconnect = {
|
|
||||||
// FIXME: Notify impl that service is dead and might need restarted
|
|
||||||
Log.e(TAG, "ImageDecoder Died! :(")
|
|
||||||
}
|
|
||||||
// FIXME: Unbind this at some point maybe
|
|
||||||
bindService(
|
|
||||||
Intent(this, ImageDecoderService::class.java),
|
|
||||||
connector,
|
|
||||||
Context.BIND_AUTO_CREATE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
external fun nativeInit()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
init {
|
|
||||||
System.loadLibrary("webcontent")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.View
|
|
||||||
|
|
||||||
// FIXME: This should (eventually) implement NestedScrollingChild3 and ScrollingView
|
|
||||||
class WebView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
|
|
||||||
private val viewImpl = WebViewImplementation(this)
|
|
||||||
private lateinit var contentBitmap: Bitmap
|
|
||||||
var onLoadStart: (url: String, isRedirect: Boolean) -> Unit = { _, _ -> }
|
|
||||||
|
|
||||||
fun initialize(resourceDir: String) {
|
|
||||||
viewImpl.initialize(resourceDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dispose() {
|
|
||||||
viewImpl.dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadURL(url: String) {
|
|
||||||
viewImpl.loadURL(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
||||||
super.onSizeChanged(w, h, oldw, oldh)
|
|
||||||
contentBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
|
||||||
|
|
||||||
val pixelDensity = context.resources.displayMetrics.density
|
|
||||||
viewImpl.setDevicePixelRatio(pixelDensity)
|
|
||||||
|
|
||||||
// FIXME: Account for scroll offset when view supports scrolling
|
|
||||||
viewImpl.setViewportGeometry(w, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas) {
|
|
||||||
super.onDraw(canvas)
|
|
||||||
|
|
||||||
viewImpl.drawIntoBitmap(contentBitmap);
|
|
||||||
canvas.drawBitmap(contentBitmap, 0f, 0f, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.ServiceConnection
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper around WebView::ViewImplementation for use by Kotlin
|
|
||||||
*/
|
|
||||||
class WebViewImplementation(private val view: WebView) {
|
|
||||||
// Instance Pointer to native object, very unsafe :)
|
|
||||||
private var nativeInstance: Long = 0
|
|
||||||
private lateinit var resourceDir: String
|
|
||||||
private lateinit var connection: ServiceConnection
|
|
||||||
|
|
||||||
fun initialize(resourceDir: String) {
|
|
||||||
this.resourceDir = resourceDir
|
|
||||||
nativeInstance = nativeObjectInit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dispose() {
|
|
||||||
nativeObjectDispose(nativeInstance)
|
|
||||||
nativeInstance = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadURL(url: String) {
|
|
||||||
nativeLoadURL(nativeInstance, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun drawIntoBitmap(bitmap: Bitmap) {
|
|
||||||
nativeDrawIntoBitmap(nativeInstance, bitmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setViewportGeometry(w: Int, h: Int) {
|
|
||||||
nativeSetViewportGeometry(nativeInstance, w, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setDevicePixelRatio(ratio: Float) {
|
|
||||||
nativeSetDevicePixelRatio(nativeInstance, ratio)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Functions called from native code
|
|
||||||
fun bindWebContentService(ipcFd: Int) {
|
|
||||||
val connector = LadybirdServiceConnection(ipcFd, resourceDir)
|
|
||||||
connector.onDisconnect = {
|
|
||||||
// FIXME: Notify impl that service is dead and might need restarted
|
|
||||||
Log.e("WebContentView", "WebContent Died! :(")
|
|
||||||
}
|
|
||||||
// FIXME: Unbind this at some point maybe
|
|
||||||
view.context.bindService(
|
|
||||||
Intent(view.context, WebContentService::class.java),
|
|
||||||
connector,
|
|
||||||
Context.BIND_AUTO_CREATE
|
|
||||||
)
|
|
||||||
connection = connector
|
|
||||||
}
|
|
||||||
|
|
||||||
fun invalidateLayout() {
|
|
||||||
view.requestLayout()
|
|
||||||
view.invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onLoadStart(url: String, isRedirect: Boolean) {
|
|
||||||
view.onLoadStart(url, isRedirect)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Functions implemented in native code
|
|
||||||
private external fun nativeObjectInit(): Long
|
|
||||||
private external fun nativeObjectDispose(instance: Long)
|
|
||||||
|
|
||||||
private external fun nativeDrawIntoBitmap(instance: Long, bitmap: Bitmap)
|
|
||||||
private external fun nativeSetViewportGeometry(instance: Long, w: Int, h: Int)
|
|
||||||
private external fun nativeSetDevicePixelRatio(instance: Long, ratio: Float)
|
|
||||||
private external fun nativeLoadURL(instance: Long, url: String)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/*
|
|
||||||
* We use a static class initializer to allow the native code to cache some
|
|
||||||
* field offsets. This native function looks up and caches interesting
|
|
||||||
* class/field/method IDs. Throws on failure.
|
|
||||||
*/
|
|
||||||
private external fun nativeClassInit()
|
|
||||||
|
|
||||||
init {
|
|
||||||
nativeClassInit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,170 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path
|
|
||||||
android:fillColor="#3DDC84"
|
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M9,0L9,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,0L19,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,0L29,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,0L39,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,0L49,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,0L59,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,0L69,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,0L79,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M89,0L89,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M99,0L99,108"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,9L108,9"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,19L108,19"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,29L108,29"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,39L108,39"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,49L108,49"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,59L108,59"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,69L108,69"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,79L108,79"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,89L108,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,99L108,99"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,29L89,29"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,39L89,39"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,49L89,49"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,59L89,59"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,69L89,69"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,79L89,79"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,19L29,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,19L39,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,19L49,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,19L59,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,19L69,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,19L79,89"
|
|
||||||
android:strokeWidth="0.8"
|
|
||||||
android:strokeColor="#33FFFFFF" />
|
|
||||||
</vector>
|
|
|
@ -1,30 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="85.84757"
|
|
||||||
android:endY="92.4963"
|
|
||||||
android:startX="42.9492"
|
|
||||||
android:startY="49.59793"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="#00000000" />
|
|
||||||
</vector>
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
tools:context=".LadybirdActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/app_bar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize">
|
|
||||||
<!-- FIXME: Add Navigation, URL bar, Tab interactions, etc -->
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_scrollFlags="scroll|snap|enterAlways">
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/urlEditText"
|
|
||||||
style="@style/Widget.AppCompat.EditText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:autofillHints="url"
|
|
||||||
android:ems="10"
|
|
||||||
android:hint="@string/url_edit_default"
|
|
||||||
android:imeOptions="actionGo|actionSearch"
|
|
||||||
android:inputType="textUri"
|
|
||||||
android:singleLine="true" />
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
|
||||||
android:id="@+id/swipe_refresh"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
|
||||||
|
|
||||||
<org.serenityos.ladybird.WebView
|
|
||||||
android:id="@+id/web_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".LadybirdActivity" />
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 982 B |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 7.6 KiB |
|
@ -1,19 +0,0 @@
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
<!-- Base application theme. -->
|
|
||||||
<style name="Theme.Ladybird" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
|
||||||
<!-- Primary brand color. -->
|
|
||||||
<item name="colorPrimary">@color/purple_200</item>
|
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
|
||||||
<item name="colorOnPrimary">@color/black</item>
|
|
||||||
<!-- Secondary brand color. -->
|
|
||||||
<item name="colorSecondary">@color/teal_200</item>
|
|
||||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
|
||||||
<item name="colorOnSecondary">@color/black</item>
|
|
||||||
<!-- Status bar color. -->
|
|
||||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
|
||||||
<!-- Customize your theme here. -->
|
|
||||||
<item name="android:editTextBackground">@color/grey</item>
|
|
||||||
<item name="windowActionBar">false</item>
|
|
||||||
<item name="windowNoTitle">true</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="purple_200">#FFBB86FC</color>
|
|
||||||
<color name="purple_500">#FF6200EE</color>
|
|
||||||
<color name="purple_700">#FF3700B3</color>
|
|
||||||
<color name="teal_200">#FF03DAC5</color>
|
|
||||||
<color name="teal_700">#FF018786</color>
|
|
||||||
<color name="black">#FF000000</color>
|
|
||||||
<color name="white">#FFFFFFFF</color>
|
|
||||||
<color name="grey">#FF6B6B6B</color>
|
|
||||||
</resources>
|
|
|
@ -1,4 +0,0 @@
|
||||||
<resources>
|
|
||||||
<string name="app_name">Ladybird</string>
|
|
||||||
<string name="url_edit_default">Enter URL...</string>
|
|
||||||
</resources>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
<!-- Base application theme. -->
|
|
||||||
<style name="Theme.Ladybird" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
|
||||||
<!-- Primary brand color. -->
|
|
||||||
<item name="colorPrimary">@color/purple_500</item>
|
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
|
||||||
<item name="colorOnPrimary">@color/white</item>
|
|
||||||
<!-- Secondary brand color. -->
|
|
||||||
<item name="colorSecondary">@color/teal_200</item>
|
|
||||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
|
||||||
<item name="colorOnSecondary">@color/black</item>
|
|
||||||
<!-- Status bar color. -->
|
|
||||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
|
||||||
<!-- Customize your theme here. -->
|
|
||||||
<item name="android:editTextBackground">@color/white</item>
|
|
||||||
<item name="windowActionBar">false</item>
|
|
||||||
<item name="windowNoTitle">true</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
|
@ -1,13 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
Sample backup rules file; uncomment and customize as necessary.
|
|
||||||
See https://developer.android.com/guide/topics/data/autobackup
|
|
||||||
for details.
|
|
||||||
Note: This file is ignored for devices older that API 31
|
|
||||||
See https://developer.android.com/about/versions/12/backup-restore
|
|
||||||
-->
|
|
||||||
<full-backup-content>
|
|
||||||
<!--
|
|
||||||
<include domain="sharedpref" path="."/>
|
|
||||||
<exclude domain="sharedpref" path="device.xml"/>
|
|
||||||
-->
|
|
||||||
</full-backup-content>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
Sample data extraction rules file; uncomment and customize as necessary.
|
|
||||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
|
||||||
for details.
|
|
||||||
-->
|
|
||||||
<data-extraction-rules>
|
|
||||||
<cloud-backup>
|
|
||||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
|
||||||
<include .../>
|
|
||||||
<exclude .../>
|
|
||||||
-->
|
|
||||||
</cloud-backup>
|
|
||||||
<!--
|
|
||||||
<device-transfer>
|
|
||||||
<include .../>
|
|
||||||
<exclude .../>
|
|
||||||
</device-transfer>
|
|
||||||
-->
|
|
||||||
</data-extraction-rules>
|
|
|
@ -8,7 +8,7 @@ project(ladybird
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
if (ANDROID OR IOS)
|
if (IOS)
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
set(BUILD_SHARED_LIBS OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -79,17 +79,12 @@ add_compile_options(-DAK_DONT_REPLACE_STD)
|
||||||
add_compile_options(-Wno-expansion-to-defined)
|
add_compile_options(-Wno-expansion-to-defined)
|
||||||
add_compile_options(-Wno-user-defined-literals)
|
add_compile_options(-Wno-user-defined-literals)
|
||||||
|
|
||||||
if (ANDROID OR APPLE)
|
if (APPLE)
|
||||||
serenity_option(ENABLE_QT OFF CACHE BOOL "Build ladybird application using Qt GUI")
|
serenity_option(ENABLE_QT OFF CACHE BOOL "Build ladybird application using Qt GUI")
|
||||||
else()
|
else()
|
||||||
serenity_option(ENABLE_QT ON CACHE BOOL "Build ladybird application using Qt GUI")
|
serenity_option(ENABLE_QT ON CACHE BOOL "Build ladybird application using Qt GUI")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ANDROID AND ENABLE_QT)
|
|
||||||
message(STATUS "Disabling Qt for Android")
|
|
||||||
set(ENABLE_QT OFF CACHE BOOL "" FORCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
|
@ -158,17 +153,6 @@ elseif (APPLE)
|
||||||
-fobjc-arc
|
-fobjc-arc
|
||||||
-Wno-deprecated-anon-enum-enum-conversion # Required for CGImageCreate
|
-Wno-deprecated-anon-enum-enum-conversion # Required for CGImageCreate
|
||||||
)
|
)
|
||||||
elseif(ANDROID)
|
|
||||||
add_library(ladybird SHARED
|
|
||||||
${SOURCES}
|
|
||||||
Android/src/main/cpp/LadybirdActivity.cpp
|
|
||||||
Android/src/main/cpp/WebViewImplementationNative.cpp
|
|
||||||
Android/src/main/cpp/WebViewImplementationNativeJNI.cpp
|
|
||||||
Android/src/main/cpp/ALooperEventLoopImplementation.cpp
|
|
||||||
Android/src/main/cpp/TimerExecutorService.cpp
|
|
||||||
Android/src/main/cpp/JNIHelpers.cpp
|
|
||||||
)
|
|
||||||
target_link_libraries(ladybird PRIVATE LibArchive jnigraphics android)
|
|
||||||
else()
|
else()
|
||||||
# TODO: Check for other GUI frameworks here when we move them in-tree
|
# TODO: Check for other GUI frameworks here when we move them in-tree
|
||||||
# For now, we can export a static library of common files for chromes to link to
|
# For now, we can export a static library of common files for chromes to link to
|
||||||
|
@ -215,10 +199,6 @@ target_include_directories(headless-browser PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
target_include_directories(headless-browser PRIVATE ${SERENITY_SOURCE_DIR}/Userland/)
|
target_include_directories(headless-browser PRIVATE ${SERENITY_SOURCE_DIR}/Userland/)
|
||||||
target_link_libraries(headless-browser PRIVATE AK LibCore LibWeb LibWebView LibWebSocket LibCrypto LibFileSystem LibGemini LibHTTP LibImageDecoderClient LibJS LibGfx LibMain LibSQL LibTLS LibIPC LibDiff LibProtocol LibURL)
|
target_link_libraries(headless-browser PRIVATE AK LibCore LibWeb LibWebView LibWebSocket LibCrypto LibFileSystem LibGemini LibHTTP LibImageDecoderClient LibJS LibGfx LibMain LibSQL LibTLS LibIPC LibDiff LibProtocol LibURL)
|
||||||
|
|
||||||
if (ANDROID)
|
|
||||||
include(cmake/AndroidExtras.cmake)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_custom_target(run${LADYBIRD_CUSTOM_TARGET_SUFFIX}
|
add_custom_target(run${LADYBIRD_CUSTOM_TARGET_SUFFIX}
|
||||||
COMMAND "${CMAKE_COMMAND}" -E env "SERENITY_SOURCE_DIR=${SERENITY_SOURCE_DIR}" "$<TARGET_FILE:ladybird>" $ENV{LAGOM_ARGS}
|
COMMAND "${CMAKE_COMMAND}" -E env "SERENITY_SOURCE_DIR=${SERENITY_SOURCE_DIR}" "$<TARGET_FILE:ladybird>" $ENV{LAGOM_ARGS}
|
||||||
USES_TERMINAL
|
USES_TERMINAL
|
||||||
|
|
|
@ -6,11 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ImageCodecPlugin.h"
|
#include "ImageCodecPlugin.h"
|
||||||
#ifdef AK_OS_ANDROID
|
#include "HelperProcess.h"
|
||||||
# include <Ladybird/Android/src/main/cpp/WebContentService.h>
|
|
||||||
#else
|
|
||||||
# include "HelperProcess.h"
|
|
||||||
#endif
|
|
||||||
#include "Utilities.h"
|
#include "Utilities.h"
|
||||||
#include <LibGfx/Bitmap.h>
|
#include <LibGfx/Bitmap.h>
|
||||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||||
|
@ -23,12 +19,8 @@ ImageCodecPlugin::~ImageCodecPlugin() = default;
|
||||||
NonnullRefPtr<Core::Promise<Web::Platform::DecodedImage>> ImageCodecPlugin::decode_image(ReadonlyBytes bytes, Function<ErrorOr<void>(Web::Platform::DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected)
|
NonnullRefPtr<Core::Promise<Web::Platform::DecodedImage>> ImageCodecPlugin::decode_image(ReadonlyBytes bytes, Function<ErrorOr<void>(Web::Platform::DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected)
|
||||||
{
|
{
|
||||||
if (!m_client) {
|
if (!m_client) {
|
||||||
#ifdef AK_OS_ANDROID
|
|
||||||
m_client = MUST(bind_service<ImageDecoderClient::Client>(&bind_image_decoder_java));
|
|
||||||
#else
|
|
||||||
auto candidate_image_decoder_paths = get_paths_for_helper_process("ImageDecoder"sv).release_value_but_fixme_should_propagate_errors();
|
auto candidate_image_decoder_paths = get_paths_for_helper_process("ImageDecoder"sv).release_value_but_fixme_should_propagate_errors();
|
||||||
m_client = launch_image_decoder_process(candidate_image_decoder_paths).release_value_but_fixme_should_propagate_errors();
|
m_client = launch_image_decoder_process(candidate_image_decoder_paths).release_value_but_fixme_should_propagate_errors();
|
||||||
#endif
|
|
||||||
m_client->on_death = [&] {
|
m_client->on_death = [&] {
|
||||||
m_client = nullptr;
|
m_client = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,16 +8,7 @@ set(IMAGE_DECODER_SOURCES
|
||||||
${IMAGE_DECODER_SOURCE_DIR}/ConnectionFromClient.cpp
|
${IMAGE_DECODER_SOURCE_DIR}/ConnectionFromClient.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ANDROID)
|
add_library(imagedecoder STATIC ${IMAGE_DECODER_SOURCES})
|
||||||
add_library(imagedecoder SHARED
|
|
||||||
${IMAGE_DECODER_SOURCES}
|
|
||||||
../Android/src/main/cpp/ImageDecoderService.cpp
|
|
||||||
../Android/src/main/cpp/LadybirdServiceBaseJNI.cpp
|
|
||||||
../Utilities.cpp
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
add_library(imagedecoder STATIC ${IMAGE_DECODER_SOURCES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(ImageDecoder main.cpp)
|
add_executable(ImageDecoder main.cpp)
|
||||||
target_link_libraries(ImageDecoder PRIVATE imagedecoder LibCore LibMain)
|
target_link_libraries(ImageDecoder PRIVATE imagedecoder LibCore LibMain)
|
||||||
|
|
|
@ -17,16 +17,7 @@ set(REQUESTSERVER_SOURCES
|
||||||
${REQUESTSERVER_SOURCE_DIR}/Protocol.cpp
|
${REQUESTSERVER_SOURCE_DIR}/Protocol.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ANDROID)
|
add_library(requestserver STATIC ${REQUESTSERVER_SOURCES})
|
||||||
add_library(requestserver SHARED
|
|
||||||
${REQUESTSERVER_SOURCES}
|
|
||||||
../Android/src/main/cpp/RequestServerService.cpp
|
|
||||||
../Android/src/main/cpp/LadybirdServiceBaseJNI.cpp
|
|
||||||
../Utilities.cpp
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
add_library(requestserver STATIC ${REQUESTSERVER_SOURCES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(RequestServer main.cpp)
|
add_executable(RequestServer main.cpp)
|
||||||
target_link_libraries(RequestServer PRIVATE requestserver)
|
target_link_libraries(RequestServer PRIVATE requestserver)
|
||||||
|
|
|
@ -41,7 +41,7 @@ if (ENABLE_QT)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set(LIB_TYPE STATIC)
|
set(LIB_TYPE STATIC)
|
||||||
if (ANDROID OR IOS)
|
if (IOS)
|
||||||
set(LIB_TYPE SHARED)
|
set(LIB_TYPE SHARED)
|
||||||
endif()
|
endif()
|
||||||
add_library(webcontent ${LIB_TYPE} ${WEBCONTENT_SOURCES})
|
add_library(webcontent ${LIB_TYPE} ${WEBCONTENT_SOURCES})
|
||||||
|
@ -64,16 +64,6 @@ else()
|
||||||
${WEBCONTENT_SOURCE_DIR}/WebDriverConnection.h
|
${WEBCONTENT_SOURCE_DIR}/WebDriverConnection.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ANDROID)
|
|
||||||
target_sources(webcontent PRIVATE
|
|
||||||
../Android/src/main/cpp/WebContentService.cpp
|
|
||||||
../Android/src/main/cpp/WebContentServiceJNI.cpp
|
|
||||||
../Android/src/main/cpp/LadybirdServiceBaseJNI.cpp
|
|
||||||
../Android/src/main/cpp/JNIHelpers.cpp
|
|
||||||
)
|
|
||||||
target_link_libraries(webcontent PRIVATE android)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(WebContent main.cpp)
|
add_executable(WebContent main.cpp)
|
||||||
target_link_libraries(WebContent PRIVATE webcontent)
|
target_link_libraries(WebContent PRIVATE webcontent)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -12,8 +12,6 @@ set(WEBWORKER_SOURCES
|
||||||
../Utilities.cpp
|
../Utilities.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# FIXME: Add Android service
|
|
||||||
|
|
||||||
add_library(webworker STATIC ${WEBWORKER_SOURCES})
|
add_library(webworker STATIC ${WEBWORKER_SOURCES})
|
||||||
|
|
||||||
target_include_directories(webworker PRIVATE ${SERENITY_SOURCE_DIR}/Userland/Services/)
|
target_include_directories(webworker PRIVATE ${SERENITY_SOURCE_DIR}/Userland/Services/)
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
# Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copy resources into tarball for inclusion in /assets of APK
|
|
||||||
#
|
|
||||||
set(LADYBIRD_RESOURCE_ROOT "${SERENITY_SOURCE_DIR}/Base/res")
|
|
||||||
macro(copy_res_folder folder)
|
|
||||||
add_custom_target(copy-${folder}
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
||||||
"${LADYBIRD_RESOURCE_ROOT}/${folder}"
|
|
||||||
"asset-bundle/res/${folder}"
|
|
||||||
)
|
|
||||||
add_dependencies(archive-assets copy-${folder})
|
|
||||||
endmacro()
|
|
||||||
add_custom_target(archive-assets COMMAND ${CMAKE_COMMAND} -E chdir asset-bundle tar czf ../ladybird-assets.tar.gz ./ )
|
|
||||||
copy_res_folder(html)
|
|
||||||
copy_res_folder(fonts)
|
|
||||||
copy_res_folder(icons)
|
|
||||||
copy_res_folder(emoji)
|
|
||||||
copy_res_folder(themes)
|
|
||||||
copy_res_folder(color-palettes)
|
|
||||||
copy_res_folder(cursor-themes)
|
|
||||||
add_custom_target(copy-autoplay-allowlist
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
|
||||||
"${SERENITY_SOURCE_DIR}/Base/home/anon/.config/BrowserAutoplayAllowlist.txt"
|
|
||||||
"asset-bundle/res/ladybird/BrowserAutoplayAllowlist.txt"
|
|
||||||
)
|
|
||||||
add_custom_target(copy-content-filters
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
|
||||||
"${SERENITY_SOURCE_DIR}/Base/home/anon/.config/BrowserContentFilters.txt"
|
|
||||||
"asset-bundle/res/ladybird/BrowserContentFilters.txt"
|
|
||||||
)
|
|
||||||
add_custom_target(copy-certs
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
|
||||||
"${Lagom_BINARY_DIR}/cacert.pem"
|
|
||||||
"asset-bundle/res/ladybird/cacert.pem"
|
|
||||||
)
|
|
||||||
add_dependencies(archive-assets copy-autoplay-allowlist copy-content-filters copy-certs)
|
|
||||||
add_custom_target(copy-assets COMMAND ${CMAKE_COMMAND} -E copy_if_different ladybird-assets.tar.gz "${CMAKE_SOURCE_DIR}/Android/src/main/assets/")
|
|
||||||
add_dependencies(copy-assets archive-assets)
|
|
||||||
add_dependencies(ladybird copy-assets)
|
|
|
@ -4,8 +4,5 @@ config("pthread_config") {
|
||||||
}
|
}
|
||||||
|
|
||||||
group("pthread") {
|
group("pthread") {
|
||||||
# On Android, bionic has built-in support for pthreads.
|
public_configs = [ ":pthread_config" ]
|
||||||
if (current_os != "android") {
|
|
||||||
public_configs = [ ":pthread_config" ]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,8 +93,8 @@ Optional<StringView> get(StringView name, [[maybe_unused]] SecureOnly secure)
|
||||||
builder.append('\0');
|
builder.append('\0');
|
||||||
// Note the explicit null terminators above.
|
// Note the explicit null terminators above.
|
||||||
|
|
||||||
// FreeBSD < 14, Android, and generic BSDs do not support secure_getenv.
|
// FreeBSD < 14, and generic BSDs do not support secure_getenv.
|
||||||
#if (defined(__FreeBSD__) && __FreeBSD__ >= 14) || (!defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID))
|
#if (defined(__FreeBSD__) && __FreeBSD__ >= 14) || !defined(AK_OS_BSD_GENERIC)
|
||||||
char* result;
|
char* result;
|
||||||
if (secure == SecureOnly::Yes) {
|
if (secure == SecureOnly::Yes) {
|
||||||
result = ::secure_getenv(builder.string_view().characters_without_null_termination());
|
result = ::secure_getenv(builder.string_view().characters_without_null_termination());
|
||||||
|
|
|
@ -67,7 +67,7 @@ ErrorOr<void> Group::sync()
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID) && !defined(AK_OS_HAIKU)
|
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_HAIKU)
|
||||||
ErrorOr<void> Group::add_group(Group& group)
|
ErrorOr<void> Group::add_group(Group& group)
|
||||||
{
|
{
|
||||||
if (group.name().is_empty())
|
if (group.name().is_empty())
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Core {
|
||||||
|
|
||||||
class Group {
|
class Group {
|
||||||
public:
|
public:
|
||||||
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID) && !defined(AK_OS_HAIKU)
|
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_HAIKU)
|
||||||
static ErrorOr<void> add_group(Group& group);
|
static ErrorOr<void> add_group(Group& group);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,7 @@ ErrorOr<String> Process::get_name()
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
return Error::from_syscall("get_process_name"sv, -rc);
|
return Error::from_syscall("get_process_name"sv, -rc);
|
||||||
return String::from_utf8(StringView { buffer, strlen(buffer) });
|
return String::from_utf8(StringView { buffer, strlen(buffer) });
|
||||||
#elif defined(AK_LIBC_GLIBC) || (defined(AK_OS_LINUX) && !defined(AK_OS_ANDROID))
|
#elif defined(AK_LIBC_GLIBC) || defined(AK_OS_LINUX)
|
||||||
return String::from_utf8(StringView { program_invocation_name, strlen(program_invocation_name) });
|
return String::from_utf8(StringView { program_invocation_name, strlen(program_invocation_name) });
|
||||||
#elif defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU)
|
#elif defined(AK_OS_BSD_GENERIC) || defined(AK_OS_HAIKU)
|
||||||
auto const* progname = getprogname();
|
auto const* progname = getprogname();
|
||||||
|
|
|
@ -374,7 +374,7 @@ ErrorOr<void> profiling_free_buffer(pid_t pid)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID)
|
#if !defined(AK_OS_BSD_GENERIC)
|
||||||
ErrorOr<Optional<struct spwd>> getspent()
|
ErrorOr<Optional<struct spwd>> getspent()
|
||||||
{
|
{
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
@ -1264,7 +1264,7 @@ ErrorOr<struct utsname> uname()
|
||||||
return uts;
|
return uts;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(AK_OS_ANDROID) && !defined(AK_OS_HAIKU)
|
#if !defined(AK_OS_HAIKU)
|
||||||
ErrorOr<void> adjtime(const struct timeval* delta, struct timeval* old_delta)
|
ErrorOr<void> adjtime(const struct timeval* delta, struct timeval* old_delta)
|
||||||
{
|
{
|
||||||
# ifdef AK_OS_SERENITY
|
# ifdef AK_OS_SERENITY
|
||||||
|
@ -1784,7 +1784,7 @@ ErrorOr<String> resolve_executable_from_environment(StringView filename, int fla
|
||||||
ErrorOr<ByteString> current_executable_path()
|
ErrorOr<ByteString> current_executable_path()
|
||||||
{
|
{
|
||||||
char path[4096] = {};
|
char path[4096] = {};
|
||||||
#if defined(AK_OS_LINUX) || defined(AK_OS_ANDROID) || defined(AK_OS_SERENITY)
|
#if defined(AK_OS_LINUX) || defined(AK_OS_SERENITY)
|
||||||
auto ret = ::readlink("/proc/self/exe", path, sizeof(path) - 1);
|
auto ret = ::readlink("/proc/self/exe", path, sizeof(path) - 1);
|
||||||
// Ignore error if it wasn't a symlink
|
// Ignore error if it wasn't a symlink
|
||||||
if (ret == -1 && errno != EINVAL)
|
if (ret == -1 && errno != EINVAL)
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
# include <Kernel/API/Jail.h>
|
# include <Kernel/API/Jail.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID)
|
#if !defined(AK_OS_BSD_GENERIC)
|
||||||
# include <shadow.h>
|
# include <shadow.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ ALWAYS_INLINE ErrorOr<void> unveil(nullptr_t, nullptr_t)
|
||||||
return unveil(StringView {}, StringView {});
|
return unveil(StringView {}, StringView {});
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID)
|
#if !defined(AK_OS_BSD_GENERIC)
|
||||||
ErrorOr<Optional<struct spwd>> getspent();
|
ErrorOr<Optional<struct spwd>> getspent();
|
||||||
ErrorOr<Optional<struct spwd>> getspnam(StringView name);
|
ErrorOr<Optional<struct spwd>> getspnam(StringView name);
|
||||||
#endif
|
#endif
|
||||||
|
@ -193,7 +193,7 @@ ErrorOr<void> utime(StringView path, Optional<struct utimbuf>);
|
||||||
ErrorOr<void> utimensat(int fd, StringView path, struct timespec const times[2], int flag);
|
ErrorOr<void> utimensat(int fd, StringView path, struct timespec const times[2], int flag);
|
||||||
ErrorOr<struct utsname> uname();
|
ErrorOr<struct utsname> uname();
|
||||||
ErrorOr<Array<int, 2>> pipe2(int flags);
|
ErrorOr<Array<int, 2>> pipe2(int flags);
|
||||||
#if !defined(AK_OS_ANDROID) && !defined(AK_OS_HAIKU)
|
#if !defined(AK_OS_HAIKU)
|
||||||
ErrorOr<void> adjtime(const struct timeval* delta, struct timeval* old_delta);
|
ErrorOr<void> adjtime(const struct timeval* delta, struct timeval* old_delta);
|
||||||
#endif
|
#endif
|
||||||
enum class SearchInPath {
|
enum class SearchInPath {
|
||||||
|
|
|
@ -25,8 +25,6 @@ namespace Web {
|
||||||
|
|
||||||
#if defined(AK_OS_SERENITY)
|
#if defined(AK_OS_SERENITY)
|
||||||
# define OS_STRING "SerenityOS"
|
# define OS_STRING "SerenityOS"
|
||||||
#elif defined(AK_OS_ANDROID)
|
|
||||||
# define OS_STRING "Android 10"
|
|
||||||
#elif defined(AK_OS_LINUX)
|
#elif defined(AK_OS_LINUX)
|
||||||
# define OS_STRING "Linux"
|
# define OS_STRING "Linux"
|
||||||
#elif defined(AK_OS_MACOS)
|
#elif defined(AK_OS_MACOS)
|
||||||
|
|