summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XPCOMEventTarget.java170
1 files changed, 170 insertions, 0 deletions
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..31eac71a66
--- /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 != null && 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 == null || !(target instanceof XPCOMEventTarget)) {
+ return false;
+ }
+
+ // Otherwise we have a real XPCOMEventTarget, so we can delegate
+ // this call to it.
+ return target.isOnCurrentThread();
+ }
+ }
+}