mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 20:33:10 +00:00
Ladybird/Android: Implement enough of WebContent to load local files
The local files can only be loaded by calling loadURL on the WebView, but it's a start.
This commit is contained in:
parent
bd131c0bf8
commit
a58ee0ecd2
|
@ -5,35 +5,34 @@
|
|||
*/
|
||||
|
||||
#include "ALooperEventLoopImplementation.h"
|
||||
#include <AK/Format.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <Ladybird/Utilities.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/Timer.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
|
||||
OwnPtr<Core::EventLoop> s_main_event_loop;
|
||||
RefPtr<Core::Timer> s_timer;
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_org_serenityos_ladybird_LadybirdActivity_initNativeCode(JNIEnv* env, jobject /* thiz */, jstring resource_dir, jobject timer_service)
|
||||
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;
|
||||
__android_log_print(ANDROID_LOG_INFO, "Ladybird", "Serenity resource dir is %s", s_serenity_resource_root.characters());
|
||||
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);
|
||||
|
||||
jobject timer_service_ref = env->NewGlobalRef(timer_service);
|
||||
JavaVM* vm = nullptr;
|
||||
jint ret = env->GetJavaVM(&vm);
|
||||
VERIFY(ret == 0);
|
||||
Core::EventLoopManager::install(*new Ladybird::ALooperEventLoopManager(vm, timer_service_ref));
|
||||
s_main_event_loop = make<Core::EventLoop>();
|
||||
|
||||
s_timer = MUST(Core::Timer::create_repeating(1000, [] {
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "Ladybird", "EventLoop is alive!");
|
||||
}));
|
||||
s_timer->start();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
|
|
|
@ -4,17 +4,101 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <android/log.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 <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Loader/FrameLoader.h>
|
||||
#include <LibWeb/Loader/ResourceLoader.h>
|
||||
#include <LibWeb/Platform/AudioCodecPluginAgnostic.h>
|
||||
#include <LibWeb/Platform/EventLoopPluginSerenity.h>
|
||||
#include <LibWebView/RequestServerAdapter.h>
|
||||
#include <LibWebView/WebSocketClientAdapter.h>
|
||||
#include <WebContent/ConnectionFromClient.h>
|
||||
#include <WebContent/PageHost.h>
|
||||
#include <jni.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_org_serenityos_ladybird_WebContentService_nativeHandleTransferSockets(JNIEnv*, jobject /* thiz */, jint ipc_socket, jint fd_passing_socket)
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_INFO, "WebContent", "New binding received, sockets %d and %d", ipc_socket, fd_passing_socket);
|
||||
::close(ipc_socket);
|
||||
::close(fd_passing_socket);
|
||||
class NullResourceConnector : public Web::ResourceLoaderConnector {
|
||||
virtual void prefetch_dns(AK::URL const&) override { }
|
||||
virtual void preconnect(AK::URL const&) override { }
|
||||
|
||||
// FIXME: Create a new thread to start WebContent processing
|
||||
// Make sure to create IPC sockets *in that thread*!
|
||||
virtual RefPtr<Web::ResourceLoaderConnectorRequest> start_request(DeprecatedString const&, AK::URL const&, HashMap<DeprecatedString, DeprecatedString> const&, ReadonlyBytes, Core::ProxyData const&) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
ErrorOr<int> web_content_main(int ipc_socket, int fd_passing_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!");
|
||||
});
|
||||
|
||||
Web::FrameLoader::set_default_favicon_path(DeprecatedString::formatted("{}/res/icons/16x16/app-browser.png", s_serenity_resource_root));
|
||||
|
||||
Web::ResourceLoader::initialize(make_ref_counted<NullResourceConnector>());
|
||||
|
||||
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));
|
||||
|
||||
Web::FrameLoader::set_resource_directory_url(DeprecatedString::formatted("file://{}/res", s_serenity_resource_root));
|
||||
Web::FrameLoader::set_error_page_url(DeprecatedString::formatted("file://{}/res/html/error.html", s_serenity_resource_root));
|
||||
Web::FrameLoader::set_directory_page_url(DeprecatedString::formatted("file://{}/res/html/directory.html", s_serenity_resource_root));
|
||||
|
||||
TRY(Web::Bindings::initialize_main_thread_vm());
|
||||
|
||||
auto webcontent_socket = TRY(Core::LocalSocket::adopt_fd(ipc_socket));
|
||||
auto webcontent_client = TRY(WebContent::ConnectionFromClient::try_create(move(webcontent_socket)));
|
||||
webcontent_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(fd_passing_socket)));
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_org_serenityos_ladybird_WebContentService_nativeThreadLoop(JNIEnv*, jobject /* thiz */, jint ipc_socket, jint fd_passing_socket)
|
||||
{
|
||||
dbgln("New binding received, sockets {} and {}", ipc_socket, fd_passing_socket);
|
||||
auto ret = web_content_main(ipc_socket, fd_passing_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_WebContentService_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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <Userland/Libraries/LibWeb/Crypto/Crypto.h>
|
||||
#include <Userland/Libraries/LibWebView/ViewImplementation.h>
|
||||
#include <android/bitmap.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
|
||||
namespace {
|
||||
|
@ -32,6 +31,11 @@ public:
|
|||
{
|
||||
// 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);
|
||||
};
|
||||
}
|
||||
|
||||
virtual Gfx::IntRect viewport_rect() const override { return m_viewport_rect; }
|
||||
|
@ -49,7 +53,7 @@ public:
|
|||
|
||||
m_client_state.client = new_client;
|
||||
m_client_state.client->on_web_content_process_crash = [] {
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Ladybird", "WebContent crashed!");
|
||||
warnln("WebContent crashed!");
|
||||
// FIXME: launch a new client
|
||||
};
|
||||
|
||||
|
@ -91,11 +95,21 @@ public:
|
|||
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 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; }
|
||||
|
@ -107,6 +121,7 @@ private:
|
|||
jclass WebViewImplementationNative::global_class_reference;
|
||||
jfieldID WebViewImplementationNative::instance_pointer_field;
|
||||
jmethodID WebViewImplementationNative::bind_webcontent_method;
|
||||
jmethodID WebViewImplementationNative::invalidate_layout_method;
|
||||
JavaVM* WebViewImplementationNative::global_vm;
|
||||
|
||||
NonnullRefPtr<WebView::WebContentClient> WebViewImplementationNative::bind_web_content_client()
|
||||
|
@ -161,6 +176,11 @@ Java_org_serenityos_ladybird_WebViewImplementation_00024Companion_nativeClassIni
|
|||
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
|
||||
|
@ -168,7 +188,6 @@ Java_org_serenityos_ladybird_WebViewImplementation_nativeObjectInit(JNIEnv* env,
|
|||
{
|
||||
auto ref = env->NewGlobalRef(thiz);
|
||||
auto instance = reinterpret_cast<jlong>(new WebViewImplementationNative(ref));
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "Ladybird", "New WebViewImplementationNative at %p", reinterpret_cast<void*>(instance));
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -178,7 +197,6 @@ Java_org_serenityos_ladybird_WebViewImplementation_nativeObjectDispose(JNIEnv* e
|
|||
auto* impl = reinterpret_cast<WebViewImplementationNative*>(instance);
|
||||
env->DeleteGlobalRef(impl->java_instance());
|
||||
delete impl;
|
||||
__android_log_print(ANDROID_LOG_DEBUG, "Ladybird", "Destroyed WebViewImplementationNative at %p", reinterpret_cast<void*>(instance));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
|
@ -202,3 +220,20 @@ Java_org_serenityos_ladybird_WebViewImplementation_nativeSetViewportGeometry(JNI
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.serenityos.ladybird
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import org.serenityos.ladybird.databinding.ActivityMainBinding
|
||||
import kotlin.io.path.Path
|
||||
|
||||
class LadybirdActivity : AppCompatActivity() {
|
||||
|
||||
|
@ -19,18 +20,27 @@ class LadybirdActivity : AppCompatActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
|
||||
resourceDir = TransferAssets.transferAssets(this)
|
||||
initNativeCode(resourceDir, timerService)
|
||||
initNativeCode(resourceDir, "Ladybird", timerService)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
view = binding.webView
|
||||
view.initialize(resourceDir)
|
||||
|
||||
mainExecutor.execute {
|
||||
callNativeEventLoopForever()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
// FIXME: This is not the right place to load the homepage :^)
|
||||
val initialURL = Path(resourceDir, "res/html/misc/welcome.html").toUri().toURL()
|
||||
view.loadURL(initialURL)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
view.dispose()
|
||||
super.onDestroy()
|
||||
|
@ -43,7 +53,11 @@ class LadybirdActivity : AppCompatActivity() {
|
|||
* A native method that is implemented by the 'ladybird' native library,
|
||||
* which is packaged with this application.
|
||||
*/
|
||||
private external fun initNativeCode(resourceDir: String, timerService: TimerExecutorService)
|
||||
private external fun initNativeCode(
|
||||
resourceDir: String,
|
||||
tag: String,
|
||||
timerService: TimerExecutorService
|
||||
)
|
||||
|
||||
// FIXME: Instead of doing this, can we push a message to the message queue of the java Looper
|
||||
// when an event is pushed to the main thread, and use that to clear out the
|
||||
|
|
|
@ -14,12 +14,17 @@ import android.os.Handler
|
|||
import android.os.IBinder
|
||||
import android.os.Message
|
||||
import android.os.Messenger
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
const val MSG_TRANSFER_SOCKETS = 1
|
||||
const val MSG_SET_RESOURCE_ROOT = 1
|
||||
const val MSG_TRANSFER_SOCKETS = 2
|
||||
|
||||
class WebContentService : Service() {
|
||||
private val TAG = "WebContentService"
|
||||
|
||||
private val threadPool = Executors.newCachedThreadPool()
|
||||
private lateinit var resourceDir: String
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.i(TAG, "Creating Service")
|
||||
|
@ -40,10 +45,15 @@ class WebContentService : Service() {
|
|||
// FIXME: Handle garbage messages from wierd clients
|
||||
val ipcSocket = bundle.getParcelable<ParcelFileDescriptor>("IPC_SOCKET")!!
|
||||
val fdSocket = bundle.getParcelable<ParcelFileDescriptor>("FD_PASSING_SOCKET")!!
|
||||
nativeHandleTransferSockets(ipcSocket.detachFd(), fdSocket.detachFd())
|
||||
createThread(ipcSocket, fdSocket)
|
||||
}
|
||||
|
||||
private external fun nativeHandleTransferSockets(ipcSocket: Int, fdPassingSocket: Int)
|
||||
private fun handleSetResourceRoot(msg: Message) {
|
||||
// FIXME: Handle this being already set, not being present, etc
|
||||
resourceDir = msg.data.getString("PATH")!!
|
||||
|
||||
initNativeCode(resourceDir, TAG)
|
||||
}
|
||||
|
||||
internal class IncomingHandler(
|
||||
context: WebContentService,
|
||||
|
@ -52,6 +62,7 @@ class WebContentService : Service() {
|
|||
override fun handleMessage(msg: Message) {
|
||||
when (msg.what) {
|
||||
MSG_TRANSFER_SOCKETS -> this.owner.handleTransferSockets(msg)
|
||||
MSG_SET_RESOURCE_ROOT -> this.owner.handleSetResourceRoot(msg)
|
||||
else -> super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +73,16 @@ class WebContentService : Service() {
|
|||
return Messenger(IncomingHandler(this)).binder
|
||||
}
|
||||
|
||||
private external fun nativeThreadLoop(ipcSocket: Int, fdPassingSocket: Int)
|
||||
|
||||
private fun createThread(ipcSocket: ParcelFileDescriptor, fdSocket: ParcelFileDescriptor) {
|
||||
threadPool.execute {
|
||||
nativeThreadLoop(ipcSocket.detachFd(), fdSocket.detachFd())
|
||||
}
|
||||
}
|
||||
|
||||
private external fun initNativeCode(resourceDir: String, tagName: String);
|
||||
|
||||
companion object {
|
||||
init {
|
||||
System.loadLibrary("webcontent")
|
||||
|
|
|
@ -13,7 +13,11 @@ import android.os.Message
|
|||
import android.os.Messenger
|
||||
import android.os.ParcelFileDescriptor
|
||||
|
||||
class WebContentServiceConnection(private var ipcFd: Int, private var fdPassingFd: Int) :
|
||||
class WebContentServiceConnection(
|
||||
private var ipcFd: Int,
|
||||
private var fdPassingFd: Int,
|
||||
private var resourceDir: String
|
||||
) :
|
||||
ServiceConnection {
|
||||
var boundToWebContent: Boolean = false
|
||||
var onDisconnect: () -> Unit = {}
|
||||
|
@ -28,7 +32,11 @@ class WebContentServiceConnection(private var ipcFd: Int, private var fdPassingF
|
|||
webContentService = Messenger(svc)
|
||||
boundToWebContent = true
|
||||
|
||||
var msg = Message.obtain(null, MSG_TRANSFER_SOCKETS)
|
||||
val init = Message.obtain(null, MSG_SET_RESOURCE_ROOT)
|
||||
init.data.putString("PATH", resourceDir)
|
||||
webContentService!!.send(init)
|
||||
|
||||
val msg = Message.obtain(null, MSG_TRANSFER_SOCKETS)
|
||||
msg.data.putParcelable("IPC_SOCKET", ParcelFileDescriptor.adoptFd(ipcFd))
|
||||
msg.data.putParcelable("FD_PASSING_SOCKET", ParcelFileDescriptor.adoptFd(fdPassingFd))
|
||||
webContentService!!.send(msg)
|
||||
|
|
|
@ -11,19 +11,32 @@ import android.graphics.Bitmap
|
|||
import android.graphics.Canvas
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import java.net.URL
|
||||
|
||||
// FIXME: This should (eventually) implement NestedScrollingChild3 and ScrollingView
|
||||
class WebView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
|
||||
private val viewImpl = WebViewImplementation(context)
|
||||
private val viewImpl = WebViewImplementation(this)
|
||||
private lateinit var contentBitmap: Bitmap
|
||||
|
||||
fun initialize(resourceDir: String) {
|
||||
viewImpl.initialize(resourceDir)
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
viewImpl.dispose()
|
||||
}
|
||||
|
||||
fun loadURL(url: URL) {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -8,24 +8,24 @@ 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(
|
||||
context: Context,
|
||||
private var appContext: Context = context.applicationContext
|
||||
) {
|
||||
class WebViewImplementation(private val view: WebView) {
|
||||
// Instance Pointer to native object, very unsafe :)
|
||||
private var nativeInstance = nativeObjectInit()
|
||||
private var nativeInstance: Long = 0
|
||||
private lateinit var resourceDir: String
|
||||
private lateinit var connection: ServiceConnection
|
||||
|
||||
init {
|
||||
Log.d(
|
||||
"Ladybird",
|
||||
"New WebViewImplementation (Kotlin) with nativeInstance ${this.nativeInstance}"
|
||||
)
|
||||
fun initialize(resourceDir: String) {
|
||||
this.resourceDir = resourceDir
|
||||
nativeInstance = nativeObjectInit()
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
|
@ -33,6 +33,10 @@ class WebViewImplementation(
|
|||
nativeInstance = 0
|
||||
}
|
||||
|
||||
fun loadURL(url: URL) {
|
||||
nativeLoadURL(nativeInstance, url.toString())
|
||||
}
|
||||
|
||||
fun drawIntoBitmap(bitmap: Bitmap) {
|
||||
nativeDrawIntoBitmap(nativeInstance, bitmap)
|
||||
}
|
||||
|
@ -41,25 +45,39 @@ class WebViewImplementation(
|
|||
nativeSetViewportGeometry(nativeInstance, w, h)
|
||||
}
|
||||
|
||||
fun setDevicePixelRatio(ratio: Float) {
|
||||
nativeSetDevicePixelRatio(nativeInstance, ratio)
|
||||
}
|
||||
|
||||
// Functions called from native code
|
||||
fun bindWebContentService(ipcFd: Int, fdPassingFd: Int) {
|
||||
var connector = WebContentServiceConnection(ipcFd, fdPassingFd)
|
||||
val connector = WebContentServiceConnection(ipcFd, fdPassingFd, 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
|
||||
appContext.bindService(
|
||||
Intent(appContext, WebContentService::class.java),
|
||||
view.context.bindService(
|
||||
Intent(view.context, WebContentService::class.java),
|
||||
connector,
|
||||
Context.BIND_AUTO_CREATE
|
||||
)
|
||||
connection = connector
|
||||
}
|
||||
|
||||
fun invalidateLayout() {
|
||||
view.requestLayout()
|
||||
view.invalidate()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue