Ladybird/Android: Move JNI functions into their own files

This should be easier to work on, and keeps the layers of the native
code nice and clean cut.
This commit is contained in:
Andrew Kaster 2023-09-15 12:35:20 -06:00 committed by Andrew Kaster
parent 274fd88242
commit 315ad2d391
Notes: sideshowbarker 2024-07-17 21:16:31 +09:00
8 changed files with 256 additions and 210 deletions

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "WebViewImplementationNative.h"
#include "JNIHelpers.h"
#include <Userland/Libraries/LibGfx/Bitmap.h>
#include <Userland/Libraries/LibGfx/Painter.h>
@ -12,9 +13,8 @@
#include <android/bitmap.h>
#include <jni.h>
namespace {
Gfx::BitmapFormat to_gfx_bitmap_format(i32 f)
namespace Ladybird {
static Gfx::BitmapFormat to_gfx_bitmap_format(i32 f)
{
switch (f) {
case ANDROID_BITMAP_FORMAT_RGBA_8888:
@ -24,109 +24,82 @@ Gfx::BitmapFormat to_gfx_bitmap_format(i32 f)
}
}
class WebViewImplementationNative : public WebView::ViewImplementation {
public:
WebViewImplementationNative(jobject thiz)
: m_java_instance(thiz)
{
// NOTE: m_java_instance's global ref is controlled by the JNI bindings
create_client(WebView::EnableCallgrindProfiling::No);
WebViewImplementationNative::WebViewImplementationNative(jobject thiz)
: m_java_instance(thiz)
{
// NOTE: m_java_instance's global ref is controlled by the JNI bindings
create_client(WebView::EnableCallgrindProfiling::No);
on_ready_to_paint = [this]() {
JavaEnvironment env(global_vm);
env.get()->CallVoidMethod(m_java_instance, invalidate_layout_method);
};
}
on_ready_to_paint = [this]() {
JavaEnvironment env(global_vm);
env.get()->CallVoidMethod(m_java_instance, invalidate_layout_method);
};
}
virtual Gfx::IntRect 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 { }
void WebViewImplementationNative::create_client(WebView::EnableCallgrindProfiling)
{
m_client_state = {};
NonnullRefPtr<WebView::WebContentClient> bind_web_content_client();
auto new_client = bind_web_content_client();
virtual void create_client(WebView::EnableCallgrindProfiling) override
{
m_client_state = {};
m_client_state.client = new_client;
m_client_state.client->on_web_content_process_crash = [] {
warnln("WebContent crashed!");
// FIXME: launch a new client
};
auto new_client = bind_web_content_client();
m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid());
client().async_set_window_handle(m_client_state.client_handle);
m_client_state.client = new_client;
m_client_state.client->on_web_content_process_crash = [] {
warnln("WebContent crashed!");
// FIXME: launch a new client
};
client().async_set_device_pixels_per_css_pixel(m_device_pixel_ratio);
m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid());
client().async_set_window_handle(m_client_state.client_handle);
// FIXME: update_palette, update system fonts
}
client().async_set_device_pixels_per_css_pixel(m_device_pixel_ratio);
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);
// FIXME: update_palette, update system fonts
}
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);
void 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;
}
// 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 set_viewport_geometry(int w, int h)
{
m_viewport_rect = { { 0, 0 }, { w, h } };
client().async_set_viewport_rect(m_viewport_rect);
request_repaint();
handle_resize();
}
void WebViewImplementationNative::set_viewport_geometry(int w, int h)
{
m_viewport_rect = { { 0, 0 }, { w, h } };
client().async_set_viewport_rect(m_viewport_rect);
request_repaint();
handle_resize();
}
void set_device_pixel_ratio(float f)
{
m_device_pixel_ratio = f;
client().async_set_device_pixels_per_css_pixel(m_device_pixel_ratio);
}
static jclass global_class_reference;
static jfieldID instance_pointer_field;
static jmethodID bind_webcontent_method;
static jmethodID invalidate_layout_method;
static JavaVM* global_vm;
jobject java_instance() const { return m_java_instance; }
private:
jobject m_java_instance = nullptr;
Gfx::IntRect m_viewport_rect;
};
jclass WebViewImplementationNative::global_class_reference;
jfieldID WebViewImplementationNative::instance_pointer_field;
jmethodID WebViewImplementationNative::bind_webcontent_method;
jmethodID WebViewImplementationNative::invalidate_layout_method;
JavaVM* WebViewImplementationNative::global_vm;
void WebViewImplementationNative::set_device_pixel_ratio(float f)
{
m_device_pixel_ratio = f;
client().async_set_device_pixels_per_css_pixel(m_device_pixel_ratio);
}
NonnullRefPtr<WebView::WebContentClient> WebViewImplementationNative::bind_web_content_client()
{
JavaEnvironment env(WebViewImplementationNative::global_vm);
JavaEnvironment env(global_vm);
int socket_fds[2] {};
MUST(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
@ -151,89 +124,4 @@ NonnullRefPtr<WebView::WebContentClient> WebViewImplementationNative::bind_web_c
return new_client;
}
}
extern "C" JNIEXPORT void JNICALL
Java_org_serenityos_ladybird_WebViewImplementation_00024Companion_nativeClassInit(JNIEnv* env, jobject /* thiz */)
{
auto ret = env->GetJavaVM(&WebViewImplementationNative::global_vm);
if (ret != 0)
TODO();
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 field = env->GetFieldID(WebViewImplementationNative::global_class_reference, "nativeInstance", "J");
if (!field)
TODO();
WebViewImplementationNative::instance_pointer_field = field;
auto method = env->GetMethodID(WebViewImplementationNative::global_class_reference, "bindWebContentService", "(II)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;
}
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 = AK::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);
}