From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../org/mozilla/gecko/util/XPCOMEventTarget.java | 170 +++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java') diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java new file mode 100644 index 0000000000..f3e5248466 --- /dev/null +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java @@ -0,0 +1,170 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.util; + +import androidx.annotation.NonNull; +import org.mozilla.gecko.GeckoThread; +import org.mozilla.gecko.annotation.WrapForJNI; +import org.mozilla.gecko.mozglue.JNIObject; +import org.mozilla.geckoview.BuildConfig; + +/** + * Wrapper for nsIEventTarget, enabling seamless dispatch of java runnables to Gecko event queues. + */ +@WrapForJNI +public final class XPCOMEventTarget extends JNIObject implements IXPCOMEventTarget { + @Override + public void execute(final Runnable runnable) { + dispatchNative(new JNIRunnable(runnable)); + } + + public static synchronized IXPCOMEventTarget mainThread() { + if (mMainThread == null) { + mMainThread = new AsyncProxy("main"); + } + return mMainThread; + } + + private static IXPCOMEventTarget mMainThread = null; + + public static synchronized IXPCOMEventTarget launcherThread() { + if (mLauncherThread == null) { + mLauncherThread = new AsyncProxy("launcher"); + } + return mLauncherThread; + } + + private static IXPCOMEventTarget mLauncherThread = null; + + /** + * Runs the provided runnable on the launcher thread. If this method is called from the launcher + * thread itself, the runnable will be executed immediately and synchronously. + */ + public static void runOnLauncherThread(@NonNull final Runnable runnable) { + final IXPCOMEventTarget launcherThread = launcherThread(); + if (launcherThread.isOnCurrentThread()) { + // We're already on the launcher thread, just execute the runnable + runnable.run(); + return; + } + + launcherThread.execute(runnable); + } + + public static void assertOnLauncherThread() { + if (BuildConfig.DEBUG_BUILD && !launcherThread().isOnCurrentThread()) { + throw new AssertionError("Expected to be running on XPCOM launcher thread"); + } + } + + public static void assertNotOnLauncherThread() { + if (BuildConfig.DEBUG_BUILD && launcherThread().isOnCurrentThread()) { + throw new AssertionError("Expected to not be running on XPCOM launcher thread"); + } + } + + private static synchronized IXPCOMEventTarget getTarget(final String name) { + if (name.equals("launcher")) { + return mLauncherThread; + } else if (name.equals("main")) { + return mMainThread; + } else { + throw new RuntimeException("Attempt to assign to unknown thread named " + name); + } + } + + @WrapForJNI + private static synchronized void setTarget(final String name, final XPCOMEventTarget target) { + if (name.equals("main")) { + mMainThread = target; + } else if (name.equals("launcher")) { + mLauncherThread = target; + } else { + throw new RuntimeException("Attempt to assign to unknown thread named " + name); + } + + // Ensure that we see the right name in the Java debugger. We don't do this for mMainThread + // because its name was already set (in this context, "main" is the GeckoThread). + if (mMainThread != target) { + target.execute( + () -> { + Thread.currentThread().setName(name); + }); + } + } + + @Override + public native boolean isOnCurrentThread(); + + private native void dispatchNative(final JNIRunnable runnable); + + @WrapForJNI + private static synchronized void resolveAndDispatch(final String name, final Runnable runnable) { + getTarget(name).execute(runnable); + } + + private static native void resolveAndDispatchNative(final String name, final Runnable runnable); + + @Override + protected native void disposeNative(); + + @WrapForJNI + private static final class JNIRunnable { + JNIRunnable(final Runnable inner) { + mInner = inner; + } + + @WrapForJNI + void run() { + mInner.run(); + } + + private Runnable mInner; + } + + private static final class AsyncProxy implements IXPCOMEventTarget { + private String mTargetName; + + public AsyncProxy(final String targetName) { + mTargetName = targetName; + } + + @Override + public void execute(final Runnable runnable) { + final IXPCOMEventTarget target = XPCOMEventTarget.getTarget(mTargetName); + + if (target instanceof XPCOMEventTarget) { + target.execute(runnable); + return; + } + + GeckoThread.queueNativeCallUntil( + GeckoThread.State.JNI_READY, + XPCOMEventTarget.class, + "resolveAndDispatchNative", + String.class, + mTargetName, + Runnable.class, + runnable); + } + + @Override + public boolean isOnCurrentThread() { + final IXPCOMEventTarget target = XPCOMEventTarget.getTarget(mTargetName); + + // If target is not yet a XPCOMEventTarget then JNI is not + // initialized yet. If JNI is not initialized yet, then we cannot + // possibly be running on a target with an XPCOMEventTarget. + if (!(target instanceof XPCOMEventTarget)) { + return false; + } + + // Otherwise we have a real XPCOMEventTarget, so we can delegate + // this call to it. + return target.isOnCurrentThread(); + } + } +} -- cgit v1.2.3