summaryrefslogtreecommitdiffstats
path: root/widget/android/jni
diff options
context:
space:
mode:
Diffstat (limited to 'widget/android/jni')
-rw-r--r--widget/android/jni/Accessors.h274
-rw-r--r--widget/android/jni/Natives.h707
-rw-r--r--widget/android/jni/Refs.h953
-rw-r--r--widget/android/jni/Types.h140
-rw-r--r--widget/android/jni/Utils.cpp301
-rw-r--r--widget/android/jni/Utils.h147
-rw-r--r--widget/android/jni/moz.build24
7 files changed, 2546 insertions, 0 deletions
diff --git a/widget/android/jni/Accessors.h b/widget/android/jni/Accessors.h
new file mode 100644
index 000000000..fe2cbc9d4
--- /dev/null
+++ b/widget/android/jni/Accessors.h
@@ -0,0 +1,274 @@
+#ifndef mozilla_jni_Accessors_h__
+#define mozilla_jni_Accessors_h__
+
+#include <jni.h>
+
+#include "mozilla/jni/Refs.h"
+#include "mozilla/jni/Types.h"
+#include "mozilla/jni/Utils.h"
+#include "AndroidBridge.h"
+
+namespace mozilla {
+namespace jni {
+
+namespace detail {
+
+// Helper class to convert an arbitrary type to a jvalue, e.g. Value(123).val.
+struct Value
+{
+ Value(jboolean z) { val.z = z; }
+ Value(jbyte b) { val.b = b; }
+ Value(jchar c) { val.c = c; }
+ Value(jshort s) { val.s = s; }
+ Value(jint i) { val.i = i; }
+ Value(jlong j) { val.j = j; }
+ Value(jfloat f) { val.f = f; }
+ Value(jdouble d) { val.d = d; }
+ Value(jobject l) { val.l = l; }
+
+ jvalue val;
+};
+
+} // namespace detail
+
+using namespace detail;
+
+// Base class for Method<>, Field<>, and Constructor<>.
+class Accessor
+{
+ static void GetNsresult(JNIEnv* env, nsresult* rv)
+ {
+ if (env->ExceptionCheck()) {
+#ifdef MOZ_CHECK_JNI
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ *rv = NS_ERROR_FAILURE;
+ } else {
+ *rv = NS_OK;
+ }
+ }
+
+protected:
+ // Called after making a JNIEnv call.
+ template<class Traits>
+ static void EndAccess(const typename Traits::Owner::Context& ctx,
+ nsresult* rv)
+ {
+ if (Traits::exceptionMode == ExceptionMode::ABORT) {
+ MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
+
+ } else if (Traits::exceptionMode == ExceptionMode::NSRESULT) {
+ GetNsresult(ctx.Env(), rv);
+ }
+ }
+};
+
+
+// Member<> is used to call a JNI method given a traits class.
+template<class Traits, typename ReturnType = typename Traits::ReturnType>
+class Method : public Accessor
+{
+ typedef Accessor Base;
+ typedef typename Traits::Owner::Context Context;
+
+protected:
+ static jmethodID sID;
+
+ static void BeginAccess(const Context& ctx)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+ static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
+ "Dispatching not supported for method call");
+
+ if (sID) {
+ return;
+ }
+
+ if (Traits::isStatic) {
+ MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticMethodID(
+ ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
+ } else {
+ MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetMethodID(
+ ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
+ }
+ }
+
+ static void EndAccess(const Context& ctx, nsresult* rv)
+ {
+ return Base::EndAccess<Traits>(ctx, rv);
+ }
+
+public:
+ template<typename... Args>
+ static ReturnType Call(const Context& ctx, nsresult* rv, const Args&... args)
+ {
+ JNIEnv* const env = ctx.Env();
+ BeginAccess(ctx);
+
+ jvalue jargs[] = {
+ Value(TypeAdapter<Args>::FromNative(env, args)).val ...
+ };
+
+ auto result = TypeAdapter<ReturnType>::ToNative(env,
+ Traits::isStatic ?
+ (env->*TypeAdapter<ReturnType>::StaticCall)(
+ ctx.RawClassRef(), sID, jargs) :
+ (env->*TypeAdapter<ReturnType>::Call)(
+ ctx.Get(), sID, jargs));
+
+ EndAccess(ctx, rv);
+ return result;
+ }
+};
+
+// Define sID member.
+template<class T, typename R> jmethodID Method<T, R>::sID;
+
+
+// Specialize void because C++ forbids us from
+// using a "void" temporary result variable.
+template<class Traits>
+class Method<Traits, void> : public Method<Traits, bool>
+{
+ typedef Method<Traits, bool> Base;
+ typedef typename Traits::Owner::Context Context;
+
+public:
+ template<typename... Args>
+ static void Call(const Context& ctx, nsresult* rv,
+ const Args&... args)
+ {
+ JNIEnv* const env = ctx.Env();
+ Base::BeginAccess(ctx);
+
+ jvalue jargs[] = {
+ Value(TypeAdapter<Args>::FromNative(env, args)).val ...
+ };
+
+ if (Traits::isStatic) {
+ env->CallStaticVoidMethodA(ctx.RawClassRef(), Base::sID, jargs);
+ } else {
+ env->CallVoidMethodA(ctx.Get(), Base::sID, jargs);
+ }
+
+ Base::EndAccess(ctx, rv);
+ }
+};
+
+
+// Constructor<> is used to construct a JNI instance given a traits class.
+template<class Traits>
+class Constructor : protected Method<Traits, typename Traits::ReturnType> {
+ typedef typename Traits::Owner::Context Context;
+ typedef typename Traits::ReturnType ReturnType;
+ typedef Method<Traits, ReturnType> Base;
+
+public:
+ template<typename... Args>
+ static ReturnType Call(const Context& ctx, nsresult* rv,
+ const Args&... args)
+ {
+ JNIEnv* const env = ctx.Env();
+ Base::BeginAccess(ctx);
+
+ jvalue jargs[] = {
+ Value(TypeAdapter<Args>::FromNative(env, args)).val ...
+ };
+
+ auto result = TypeAdapter<ReturnType>::ToNative(
+ env, env->NewObjectA(ctx.RawClassRef(), Base::sID, jargs));
+
+ Base::EndAccess(ctx, rv);
+ return result;
+ }
+};
+
+
+// Field<> is used to access a JNI field given a traits class.
+template<class Traits>
+class Field : public Accessor
+{
+ typedef Accessor Base;
+ typedef typename Traits::Owner::Context Context;
+ typedef typename Traits::ReturnType GetterType;
+ typedef typename Traits::SetterType SetterType;
+
+private:
+
+ static jfieldID sID;
+
+ static void BeginAccess(const Context& ctx)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+ static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
+ "Dispatching not supported for field access");
+
+ if (sID) {
+ return;
+ }
+
+ if (Traits::isStatic) {
+ MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticFieldID(
+ ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
+ } else {
+ MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetFieldID(
+ ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
+ }
+ }
+
+ static void EndAccess(const Context& ctx, nsresult* rv)
+ {
+ return Base::EndAccess<Traits>(ctx, rv);
+ }
+
+public:
+ static GetterType Get(const Context& ctx, nsresult* rv)
+ {
+ JNIEnv* const env = ctx.Env();
+ BeginAccess(ctx);
+
+ auto result = TypeAdapter<GetterType>::ToNative(
+ env, Traits::isStatic ?
+
+ (env->*TypeAdapter<GetterType>::StaticGet)
+ (ctx.RawClassRef(), sID) :
+
+ (env->*TypeAdapter<GetterType>::Get)
+ (ctx.Get(), sID));
+
+ EndAccess(ctx, rv);
+ return result;
+ }
+
+ static void Set(const Context& ctx, nsresult* rv, SetterType val)
+ {
+ JNIEnv* const env = ctx.Env();
+ BeginAccess(ctx);
+
+ if (Traits::isStatic) {
+ (env->*TypeAdapter<SetterType>::StaticSet)(
+ ctx.RawClassRef(), sID,
+ TypeAdapter<SetterType>::FromNative(env, val));
+ } else {
+ (env->*TypeAdapter<SetterType>::Set)(
+ ctx.Get(), sID,
+ TypeAdapter<SetterType>::FromNative(env, val));
+ }
+
+ EndAccess(ctx, rv);
+ }
+};
+
+// Define sID member.
+template<class T> jfieldID Field<T>::sID;
+
+
+// Define the sClassRef member declared in Refs.h and
+// used by Method and Field above.
+template<class C, typename T> jclass Context<C, T>::sClassRef;
+
+} // namespace jni
+} // namespace mozilla
+
+#endif // mozilla_jni_Accessors_h__
diff --git a/widget/android/jni/Natives.h b/widget/android/jni/Natives.h
new file mode 100644
index 000000000..511d96a87
--- /dev/null
+++ b/widget/android/jni/Natives.h
@@ -0,0 +1,707 @@
+#ifndef mozilla_jni_Natives_h__
+#define mozilla_jni_Natives_h__
+
+#include <jni.h>
+
+#include "mozilla/IndexSequence.h"
+#include "mozilla/Move.h"
+#include "mozilla/Tuple.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/jni/Accessors.h"
+#include "mozilla/jni/Refs.h"
+#include "mozilla/jni/Types.h"
+#include "mozilla/jni/Utils.h"
+
+namespace mozilla {
+namespace jni {
+
+/**
+ * C++ classes implementing instance (non-static) native methods can choose
+ * from one of two ownership models, when associating a C++ object with a Java
+ * instance.
+ *
+ * * If the C++ class inherits from mozilla::SupportsWeakPtr, weak pointers
+ * will be used. The Java instance will store and own the pointer to a
+ * WeakPtr object. The C++ class itself is otherwise not owned or directly
+ * referenced. To attach a Java instance to a C++ instance, pass in a pointer
+ * to the C++ class (i.e. MyClass*).
+ *
+ * class MyClass : public SupportsWeakPtr<MyClass>
+ * , public MyJavaClass::Natives<MyClass>
+ * {
+ * // ...
+ *
+ * public:
+ * MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MyClass)
+ * using MyJavaClass::Natives<MyClass>::Dispose;
+ *
+ * void AttachTo(const MyJavaClass::LocalRef& instance)
+ * {
+ * MyJavaClass::Natives<MyClass>::AttachInstance(instance, this);
+ *
+ * // "instance" does NOT own "this", so the C++ object
+ * // lifetime is separate from the Java object lifetime.
+ * }
+ * };
+ *
+ * * If the C++ class doesn't inherit from mozilla::SupportsWeakPtr, the Java
+ * instance will store and own a pointer to the C++ object itself. This
+ * pointer must not be stored or deleted elsewhere. To attach a Java instance
+ * to a C++ instance, pass in a reference to a UniquePtr of the C++ class
+ * (i.e. UniquePtr<MyClass>).
+ *
+ * class MyClass : public MyJavaClass::Natives<MyClass>
+ * {
+ * // ...
+ *
+ * public:
+ * using MyJavaClass::Natives<MyClass>::Dispose;
+ *
+ * static void AttachTo(const MyJavaClass::LocalRef& instance)
+ * {
+ * MyJavaClass::Natives<MyClass>::AttachInstance(
+ * instance, mozilla::MakeUnique<MyClass>());
+ *
+ * // "instance" owns the newly created C++ object, so the C++
+ * // object is destroyed as soon as instance.dispose() is called.
+ * }
+ * };
+ */
+
+namespace detail {
+
+inline uintptr_t CheckNativeHandle(JNIEnv* env, uintptr_t handle)
+{
+ if (!handle) {
+ if (!env->ExceptionCheck()) {
+ ThrowException(env, "java/lang/NullPointerException",
+ "Null native pointer");
+ }
+ return 0;
+ }
+ return handle;
+}
+
+template<class Impl, bool UseWeakPtr = mozilla::IsBaseOf<
+ SupportsWeakPtr<Impl>, Impl>::value /* = false */>
+struct NativePtr
+{
+ static Impl* Get(JNIEnv* env, jobject instance)
+ {
+ return reinterpret_cast<Impl*>(CheckNativeHandle(
+ env, GetNativeHandle(env, instance)));
+ }
+
+ template<class LocalRef>
+ static Impl* Get(const LocalRef& instance)
+ {
+ return Get(instance.Env(), instance.Get());
+ }
+
+ template<class LocalRef>
+ static void Set(const LocalRef& instance, UniquePtr<Impl>&& ptr)
+ {
+ Clear(instance);
+ SetNativeHandle(instance.Env(), instance.Get(),
+ reinterpret_cast<uintptr_t>(ptr.release()));
+ MOZ_CATCH_JNI_EXCEPTION(instance.Env());
+ }
+
+ template<class LocalRef>
+ static void Clear(const LocalRef& instance)
+ {
+ UniquePtr<Impl> ptr(reinterpret_cast<Impl*>(
+ GetNativeHandle(instance.Env(), instance.Get())));
+ MOZ_CATCH_JNI_EXCEPTION(instance.Env());
+
+ if (ptr) {
+ SetNativeHandle(instance.Env(), instance.Get(), 0);
+ MOZ_CATCH_JNI_EXCEPTION(instance.Env());
+ }
+ }
+};
+
+template<class Impl>
+struct NativePtr<Impl, /* UseWeakPtr = */ true>
+{
+ static Impl* Get(JNIEnv* env, jobject instance)
+ {
+ const auto ptr = reinterpret_cast<WeakPtr<Impl>*>(
+ CheckNativeHandle(env, GetNativeHandle(env, instance)));
+ if (!ptr) {
+ return nullptr;
+ }
+
+ Impl* const impl = *ptr;
+ if (!impl) {
+ ThrowException(env, "java/lang/NullPointerException",
+ "Native object already released");
+ }
+ return impl;
+ }
+
+ template<class LocalRef>
+ static Impl* Get(const LocalRef& instance)
+ {
+ return Get(instance.Env(), instance.Get());
+ }
+
+ template<class LocalRef>
+ static void Set(const LocalRef& instance, Impl* ptr)
+ {
+ Clear(instance);
+ SetNativeHandle(instance.Env(), instance.Get(),
+ reinterpret_cast<uintptr_t>(new WeakPtr<Impl>(ptr)));
+ MOZ_CATCH_JNI_EXCEPTION(instance.Env());
+ }
+
+ template<class LocalRef>
+ static void Clear(const LocalRef& instance)
+ {
+ const auto ptr = reinterpret_cast<WeakPtr<Impl>*>(
+ GetNativeHandle(instance.Env(), instance.Get()));
+ MOZ_CATCH_JNI_EXCEPTION(instance.Env());
+
+ if (ptr) {
+ SetNativeHandle(instance.Env(), instance.Get(), 0);
+ MOZ_CATCH_JNI_EXCEPTION(instance.Env());
+ delete ptr;
+ }
+ }
+};
+
+} // namespace detail
+
+using namespace detail;
+
+/**
+ * For JNI native methods that are dispatched to a proxy, i.e. using
+ * @WrapForJNI(dispatchTo = "proxy"), the implementing C++ class must provide a
+ * OnNativeCall member. Subsequently, every native call is automatically
+ * wrapped in a functor object, and the object is passed to OnNativeCall. The
+ * OnNativeCall implementation can choose to invoke the call, save it, dispatch
+ * it to a different thread, etc. Each copy of functor may only be invoked
+ * once.
+ *
+ * class MyClass : public MyJavaClass::Natives<MyClass>
+ * {
+ * // ...
+ *
+ * template<class Functor>
+ * class ProxyRunnable final : public Runnable
+ * {
+ * Functor mCall;
+ * public:
+ * ProxyRunnable(Functor&& call) : mCall(mozilla::Move(call)) {}
+ * virtual void run() override { mCall(); }
+ * };
+ *
+ * public:
+ * template<class Functor>
+ * static void OnNativeCall(Functor&& call)
+ * {
+ * RunOnAnotherThread(new ProxyRunnable(mozilla::Move(call)));
+ * }
+ * };
+ */
+
+namespace detail {
+
+// ProxyArg is used to handle JNI ref arguments for proxies. Because a proxied
+// call may happen outside of the original JNI native call, we must save all
+// JNI ref arguments as global refs to avoid the arguments going out of scope.
+template<typename T>
+struct ProxyArg
+{
+ static_assert(mozilla::IsPod<T>::value, "T must be primitive type");
+
+ // Primitive types can be saved by value.
+ typedef T Type;
+ typedef typename TypeAdapter<T>::JNIType JNIType;
+
+ static void Clear(JNIEnv* env, Type&) {}
+
+ static Type From(JNIEnv* env, JNIType val)
+ {
+ return TypeAdapter<T>::ToNative(env, val);
+ }
+};
+
+template<class C, typename T>
+struct ProxyArg<Ref<C, T>>
+{
+ // Ref types need to be saved by global ref.
+ typedef typename C::GlobalRef Type;
+ typedef typename TypeAdapter<Ref<C, T>>::JNIType JNIType;
+
+ static void Clear(JNIEnv* env, Type& ref) { ref.Clear(env); }
+
+ static Type From(JNIEnv* env, JNIType val)
+ {
+ return Type(env, C::Ref::From(val));
+ }
+};
+
+template<typename C> struct ProxyArg<const C&> : ProxyArg<C> {};
+template<> struct ProxyArg<StringParam> : ProxyArg<String::Ref> {};
+template<class C> struct ProxyArg<LocalRef<C>> : ProxyArg<typename C::Ref> {};
+
+// ProxyNativeCall implements the functor object that is passed to OnNativeCall
+template<class Impl, class Owner, bool IsStatic,
+ bool HasThisArg /* has instance/class local ref in the call */,
+ typename... Args>
+class ProxyNativeCall : public AbstractCall
+{
+ // "this arg" refers to the Class::LocalRef (for static methods) or
+ // Owner::LocalRef (for instance methods) that we optionally (as indicated
+ // by HasThisArg) pass into the destination C++ function.
+ typedef typename mozilla::Conditional<IsStatic,
+ Class, Owner>::Type ThisArgClass;
+ typedef typename mozilla::Conditional<IsStatic,
+ jclass, jobject>::Type ThisArgJNIType;
+
+ // Type signature of the destination C++ function, which matches the
+ // Method template parameter in NativeStubImpl::Wrap.
+ typedef typename mozilla::Conditional<IsStatic,
+ typename mozilla::Conditional<HasThisArg,
+ void (*) (const Class::LocalRef&, Args...),
+ void (*) (Args...)>::Type,
+ typename mozilla::Conditional<HasThisArg,
+ void (Impl::*) (const typename Owner::LocalRef&, Args...),
+ void (Impl::*) (Args...)>::Type>::Type NativeCallType;
+
+ // Destination C++ function.
+ NativeCallType mNativeCall;
+ // Saved this arg.
+ typename ThisArgClass::GlobalRef mThisArg;
+ // Saved arguments.
+ mozilla::Tuple<typename ProxyArg<Args>::Type...> mArgs;
+
+ // We cannot use IsStatic and HasThisArg directly (without going through
+ // extra hoops) because GCC complains about invalid overloads, so we use
+ // another pair of template parameters, Static and ThisArg.
+
+ template<bool Static, bool ThisArg, size_t... Indices>
+ typename mozilla::EnableIf<Static && ThisArg, void>::Type
+ Call(const Class::LocalRef& cls,
+ mozilla::IndexSequence<Indices...>) const
+ {
+ (*mNativeCall)(cls, mozilla::Get<Indices>(mArgs)...);
+ }
+
+ template<bool Static, bool ThisArg, size_t... Indices>
+ typename mozilla::EnableIf<Static && !ThisArg, void>::Type
+ Call(const Class::LocalRef& cls,
+ mozilla::IndexSequence<Indices...>) const
+ {
+ (*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
+ }
+
+ template<bool Static, bool ThisArg, size_t... Indices>
+ typename mozilla::EnableIf<!Static && ThisArg, void>::Type
+ Call(const typename Owner::LocalRef& inst,
+ mozilla::IndexSequence<Indices...>) const
+ {
+ Impl* const impl = NativePtr<Impl>::Get(inst);
+ MOZ_CATCH_JNI_EXCEPTION(inst.Env());
+ (impl->*mNativeCall)(inst, mozilla::Get<Indices>(mArgs)...);
+ }
+
+ template<bool Static, bool ThisArg, size_t... Indices>
+ typename mozilla::EnableIf<!Static && !ThisArg, void>::Type
+ Call(const typename Owner::LocalRef& inst,
+ mozilla::IndexSequence<Indices...>) const
+ {
+ Impl* const impl = NativePtr<Impl>::Get(inst);
+ MOZ_CATCH_JNI_EXCEPTION(inst.Env());
+ (impl->*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
+ }
+
+ template<size_t... Indices>
+ void Clear(JNIEnv* env, mozilla::IndexSequence<Indices...>)
+ {
+ int dummy[] = {
+ (ProxyArg<Args>::Clear(env, Get<Indices>(mArgs)), 0)...
+ };
+ mozilla::Unused << dummy;
+ }
+
+public:
+ // The class that implements the call target.
+ typedef Impl TargetClass;
+ typedef typename ThisArgClass::Param ThisArgType;
+
+ static const bool isStatic = IsStatic;
+
+ ProxyNativeCall(ThisArgJNIType thisArg,
+ NativeCallType nativeCall,
+ JNIEnv* env,
+ typename ProxyArg<Args>::JNIType... args)
+ : mNativeCall(nativeCall)
+ , mThisArg(env, ThisArgClass::Ref::From(thisArg))
+ , mArgs(ProxyArg<Args>::From(env, args)...)
+ {}
+
+ ProxyNativeCall(ProxyNativeCall&&) = default;
+ ProxyNativeCall(const ProxyNativeCall&) = default;
+
+ // Get class ref for static calls or object ref for instance calls.
+ typename ThisArgClass::Param GetThisArg() const { return mThisArg; }
+
+ // Return if target is the given function pointer / pointer-to-member.
+ // Because we can only compare pointers of the same type, we use a
+ // templated overload that is chosen only if given a different type of
+ // pointer than our target pointer type.
+ bool IsTarget(NativeCallType call) const { return call == mNativeCall; }
+ template<typename T> bool IsTarget(T&&) const { return false; }
+
+ // Redirect the call to another function / class member with the same
+ // signature as the original target. Crash if given a wrong signature.
+ void SetTarget(NativeCallType call) { mNativeCall = call; }
+ template<typename T> void SetTarget(T&&) const { MOZ_CRASH(); }
+
+ void operator()() override
+ {
+ JNIEnv* const env = GetEnvForThread();
+ typename ThisArgClass::LocalRef thisArg(env, mThisArg);
+ Call<IsStatic, HasThisArg>(
+ thisArg, typename IndexSequenceFor<Args...>::Type());
+
+ // Clear all saved global refs. We do this after the call is invoked,
+ // and not inside the destructor because we already have a JNIEnv here,
+ // so it's more efficient to clear out the saved args here. The
+ // downside is that the call can only be invoked once.
+ Clear(env, typename IndexSequenceFor<Args...>::Type());
+ mThisArg.Clear(env);
+ }
+};
+
+template<class Impl, bool HasThisArg, typename... Args>
+struct Dispatcher
+{
+ template<class Traits, bool IsStatic = Traits::isStatic,
+ typename... ProxyArgs>
+ static typename EnableIf<
+ Traits::dispatchTarget == DispatchTarget::PROXY, void>::Type
+ Run(ProxyArgs&&... args)
+ {
+ Impl::OnNativeCall(ProxyNativeCall<
+ Impl, typename Traits::Owner, IsStatic,
+ HasThisArg, Args...>(Forward<ProxyArgs>(args)...));
+ }
+
+ template<class Traits, bool IsStatic = Traits::isStatic,
+ typename ThisArg, typename... ProxyArgs>
+ static typename EnableIf<
+ Traits::dispatchTarget == DispatchTarget::GECKO, void>::Type
+ Run(ThisArg thisArg, ProxyArgs&&... args)
+ {
+ // For a static method, do not forward the "this arg" (i.e. the class
+ // local ref) if the implementation does not request it. This saves us
+ // a pair of calls to add/delete global ref.
+ DispatchToGeckoThread(MakeUnique<ProxyNativeCall<
+ Impl, typename Traits::Owner, IsStatic, HasThisArg,
+ Args...>>(HasThisArg || !IsStatic ? thisArg : nullptr,
+ Forward<ProxyArgs>(args)...));
+ }
+
+ template<class Traits, bool IsStatic = false, typename... ProxyArgs>
+ static typename EnableIf<
+ Traits::dispatchTarget == DispatchTarget::CURRENT, void>::Type
+ Run(ProxyArgs&&... args) {}
+};
+
+} // namespace detail
+
+// Wrapper methods that convert arguments from the JNI types to the native
+// types, e.g. from jobject to jni::Object::Ref. For instance methods, the
+// wrapper methods also convert calls to calls on objects.
+//
+// We need specialization for static/non-static because the two have different
+// signatures (jobject vs jclass and Impl::*Method vs *Method).
+// We need specialization for return type, because void return type requires
+// us to not deal with the return value.
+
+// Bug 1207642 - Work around Dalvik bug by realigning stack on JNI entry
+#ifdef __i386__
+#define MOZ_JNICALL JNICALL __attribute__((force_align_arg_pointer))
+#else
+#define MOZ_JNICALL JNICALL
+#endif
+
+template<class Traits, class Impl, class Args = typename Traits::Args>
+class NativeStub;
+
+template<class Traits, class Impl, typename... Args>
+class NativeStub<Traits, Impl, jni::Args<Args...>>
+{
+ using Owner = typename Traits::Owner;
+ using ReturnType = typename Traits::ReturnType;
+
+ static constexpr bool isStatic = Traits::isStatic;
+ static constexpr bool isVoid = mozilla::IsVoid<ReturnType>::value;
+
+ struct VoidType { using JNIType = void; };
+ using ReturnJNIType = typename Conditional<
+ isVoid, VoidType, TypeAdapter<ReturnType>>::Type::JNIType;
+
+ using ReturnTypeForNonVoidInstance = typename Conditional<
+ !isStatic && !isVoid, ReturnType, VoidType>::Type;
+ using ReturnTypeForVoidInstance = typename Conditional<
+ !isStatic && isVoid, ReturnType, VoidType&>::Type;
+ using ReturnTypeForNonVoidStatic = typename Conditional<
+ isStatic && !isVoid, ReturnType, VoidType>::Type;
+ using ReturnTypeForVoidStatic = typename Conditional<
+ isStatic && isVoid, ReturnType, VoidType&>::Type;
+
+ static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT || isVoid,
+ "Dispatched calls must have void return type");
+
+public:
+ // Non-void instance method
+ template<ReturnTypeForNonVoidInstance (Impl::*Method) (Args...)>
+ static MOZ_JNICALL ReturnJNIType
+ Wrap(JNIEnv* env, jobject instance,
+ typename TypeAdapter<Args>::JNIType... args)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ Impl* const impl = NativePtr<Impl>::Get(env, instance);
+ if (!impl) {
+ // There is a pending JNI exception at this point.
+ return ReturnJNIType();
+ }
+ return TypeAdapter<ReturnType>::FromNative(env,
+ (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...));
+ }
+
+ // Non-void instance method with instance reference
+ template<ReturnTypeForNonVoidInstance (Impl::*Method)
+ (const typename Owner::LocalRef&, Args...)>
+ static MOZ_JNICALL ReturnJNIType
+ Wrap(JNIEnv* env, jobject instance,
+ typename TypeAdapter<Args>::JNIType... args)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ Impl* const impl = NativePtr<Impl>::Get(env, instance);
+ if (!impl) {
+ // There is a pending JNI exception at this point.
+ return ReturnJNIType();
+ }
+ auto self = Owner::LocalRef::Adopt(env, instance);
+ const auto res = TypeAdapter<ReturnType>::FromNative(env,
+ (impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...));
+ self.Forget();
+ return res;
+ }
+
+ // Void instance method
+ template<ReturnTypeForVoidInstance (Impl::*Method) (Args...)>
+ static MOZ_JNICALL void
+ Wrap(JNIEnv* env, jobject instance,
+ typename TypeAdapter<Args>::JNIType... args)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
+ Dispatcher<Impl, /* HasThisArg */ false, Args...>::
+ template Run<Traits>(instance, Method, env, args...);
+ return;
+ }
+
+ Impl* const impl = NativePtr<Impl>::Get(env, instance);
+ if (!impl) {
+ // There is a pending JNI exception at this point.
+ return;
+ }
+ (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...);
+ }
+
+ // Void instance method with instance reference
+ template<ReturnTypeForVoidInstance (Impl::*Method)
+ (const typename Owner::LocalRef&, Args...)>
+ static MOZ_JNICALL void
+ Wrap(JNIEnv* env, jobject instance,
+ typename TypeAdapter<Args>::JNIType... args)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
+ Dispatcher<Impl, /* HasThisArg */ true, Args...>::
+ template Run<Traits>(instance, Method, env, args...);
+ return;
+ }
+
+ Impl* const impl = NativePtr<Impl>::Get(env, instance);
+ if (!impl) {
+ // There is a pending JNI exception at this point.
+ return;
+ }
+ auto self = Owner::LocalRef::Adopt(env, instance);
+ (impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...);
+ self.Forget();
+ }
+
+ // Overload for DisposeNative
+ template<ReturnTypeForVoidInstance (*DisposeNative)
+ (const typename Owner::LocalRef&)>
+ static MOZ_JNICALL void
+ Wrap(JNIEnv* env, jobject instance)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
+ using LocalRef = typename Owner::LocalRef;
+ Dispatcher<Impl, /* HasThisArg */ false, const LocalRef&>::
+ template Run<Traits, /* IsStatic */ true>(
+ /* ThisArg */ nullptr, DisposeNative, env, instance);
+ return;
+ }
+
+ auto self = Owner::LocalRef::Adopt(env, instance);
+ (Impl::DisposeNative)(self);
+ self.Forget();
+ }
+
+ // Non-void static method
+ template<ReturnTypeForNonVoidStatic (*Method) (Args...)>
+ static MOZ_JNICALL ReturnJNIType
+ Wrap(JNIEnv* env, jclass, typename TypeAdapter<Args>::JNIType... args)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ return TypeAdapter<ReturnType>::FromNative(env,
+ (*Method)(TypeAdapter<Args>::ToNative(env, args)...));
+ }
+
+ // Non-void static method with class reference
+ template<ReturnTypeForNonVoidStatic (*Method)
+ (const Class::LocalRef&, Args...)>
+ static MOZ_JNICALL ReturnJNIType
+ Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ auto clazz = Class::LocalRef::Adopt(env, cls);
+ const auto res = TypeAdapter<ReturnType>::FromNative(env,
+ (*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...));
+ clazz.Forget();
+ return res;
+ }
+
+ // Void static method
+ template<ReturnTypeForVoidStatic (*Method) (Args...)>
+ static MOZ_JNICALL void
+ Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
+ Dispatcher<Impl, /* HasThisArg */ false, Args...>::
+ template Run<Traits>(cls, Method, env, args...);
+ return;
+ }
+
+ (*Method)(TypeAdapter<Args>::ToNative(env, args)...);
+ }
+
+ // Void static method with class reference
+ template<ReturnTypeForVoidStatic (*Method)
+ (const Class::LocalRef&, Args...)>
+ static MOZ_JNICALL void
+ Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
+ {
+ MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
+
+ if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
+ Dispatcher<Impl, /* HasThisArg */ true, Args...>::
+ template Run<Traits>(cls, Method, env, args...);
+ return;
+ }
+
+ auto clazz = Class::LocalRef::Adopt(env, cls);
+ (*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...);
+ clazz.Forget();
+ }
+};
+
+// Generate a JNINativeMethod from a native
+// method's traits class and a wrapped stub.
+template<class Traits, typename Ret, typename... Args>
+constexpr JNINativeMethod MakeNativeMethod(MOZ_JNICALL Ret (*stub)(JNIEnv*, Args...))
+{
+ return {
+ Traits::name,
+ Traits::signature,
+ reinterpret_cast<void*>(stub)
+ };
+}
+
+// Class inherited by implementing class.
+template<class Cls, class Impl>
+class NativeImpl
+{
+ typedef typename Cls::template Natives<Impl> Natives;
+
+ static bool sInited;
+
+public:
+ static void Init() {
+ if (sInited) {
+ return;
+ }
+ const auto& ctx = typename Cls::Context();
+ ctx.Env()->RegisterNatives(
+ ctx.ClassRef(), Natives::methods,
+ sizeof(Natives::methods) / sizeof(Natives::methods[0]));
+ MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
+ sInited = true;
+ }
+
+protected:
+
+ // Associate a C++ instance with a Java instance.
+ static void AttachNative(const typename Cls::LocalRef& instance,
+ SupportsWeakPtr<Impl>* ptr)
+ {
+ static_assert(mozilla::IsBaseOf<SupportsWeakPtr<Impl>, Impl>::value,
+ "Attach with UniquePtr&& when not using WeakPtr");
+ return NativePtr<Impl>::Set(instance, static_cast<Impl*>(ptr));
+ }
+
+ static void AttachNative(const typename Cls::LocalRef& instance,
+ UniquePtr<Impl>&& ptr)
+ {
+ static_assert(!mozilla::IsBaseOf<SupportsWeakPtr<Impl>, Impl>::value,
+ "Attach with SupportsWeakPtr* when using WeakPtr");
+ return NativePtr<Impl>::Set(instance, mozilla::Move(ptr));
+ }
+
+ // Get the C++ instance associated with a Java instance.
+ // There is always a pending exception if the return value is nullptr.
+ static Impl* GetNative(const typename Cls::LocalRef& instance) {
+ return NativePtr<Impl>::Get(instance);
+ }
+
+ static void DisposeNative(const typename Cls::LocalRef& instance) {
+ NativePtr<Impl>::Clear(instance);
+ }
+
+ NativeImpl() {
+ // Initialize on creation if not already initialized.
+ Init();
+ }
+};
+
+// Define static member.
+template<class C, class I>
+bool NativeImpl<C, I>::sInited;
+
+} // namespace jni
+} // namespace mozilla
+
+#endif // mozilla_jni_Natives_h__
diff --git a/widget/android/jni/Refs.h b/widget/android/jni/Refs.h
new file mode 100644
index 000000000..9f54ee5cd
--- /dev/null
+++ b/widget/android/jni/Refs.h
@@ -0,0 +1,953 @@
+#ifndef mozilla_jni_Refs_h__
+#define mozilla_jni_Refs_h__
+
+#include <jni.h>
+
+#include "mozilla/Move.h"
+#include "mozilla/jni/Utils.h"
+
+#include "nsError.h" // for nsresult
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace jni {
+
+// Wrapped object reference (e.g. jobject, jclass, etc...)
+template<class Cls, typename JNIType> class Ref;
+// Represents a calling context for JNI methods.
+template<class Cls, typename JNIType> class Context;
+// Wrapped local reference that inherits from Ref.
+template<class Cls> class LocalRef;
+// Wrapped global reference that inherits from Ref.
+template<class Cls> class GlobalRef;
+// Wrapped dangling reference that's owned by someone else.
+template<class Cls> class DependentRef;
+
+
+// Class to hold the native types of a method's arguments.
+// For example, if a method has signature (ILjava/lang/String;)V,
+// its arguments class would be jni::Args<int32_t, jni::String::Param>
+template<typename...>
+struct Args {};
+
+
+class Object;
+
+// Base class for Ref and its specializations.
+template<class Cls, typename Type>
+class Ref
+{
+ template<class C, typename T> friend class Ref;
+
+ using Self = Ref<Cls, Type>;
+ using bool_type = void (Self::*)() const;
+ void non_null_reference() const {}
+
+ // A Cls-derivative that allows copying
+ // (e.g. when acting as a return value).
+ struct CopyableCtx : public Context<Cls, Type>
+ {
+ CopyableCtx(JNIEnv* env, Type instance)
+ : Context<Cls, Type>(env, instance)
+ {}
+
+ CopyableCtx(const CopyableCtx& cls)
+ : Context<Cls, Type>(cls.Env(), cls.Get())
+ {}
+ };
+
+ // Private copy constructor so that there's no danger of assigning a
+ // temporary LocalRef/GlobalRef to a Ref, and potentially use the Ref
+ // after the source had been freed.
+ Ref(const Ref&) = default;
+
+protected:
+ static JNIEnv* FindEnv()
+ {
+ return Cls::callingThread == CallingThread::GECKO ?
+ GetGeckoThreadEnv() : GetEnvForThread();
+ }
+
+ Type mInstance;
+
+ // Protected jobject constructor because outside code should be using
+ // Ref::From. Using Ref::From makes it very easy to see which code is using
+ // raw JNI types for future refactoring.
+ explicit Ref(Type instance) : mInstance(instance) {}
+
+public:
+ using JNIType = Type;
+
+ // Construct a Ref form a raw JNI reference.
+ static Ref<Cls, Type> From(JNIType obj)
+ {
+ return Ref<Cls, Type>(obj);
+ }
+
+ // Construct a Ref form a generic object reference.
+ static Ref<Cls, Type> From(const Ref<Object, jobject>& obj)
+ {
+ return Ref<Cls, Type>(JNIType(obj.Get()));
+ }
+
+ MOZ_IMPLICIT Ref(decltype(nullptr)) : mInstance(nullptr) {}
+
+ // Get the raw JNI reference.
+ JNIType Get() const
+ {
+ return mInstance;
+ }
+
+ bool operator==(const Ref& other) const
+ {
+ // Treat two references of the same object as being the same.
+ return mInstance == other.mInstance || JNI_FALSE !=
+ FindEnv()->IsSameObject(mInstance, other.mInstance);
+ }
+
+ bool operator!=(const Ref& other) const
+ {
+ return !operator==(other);
+ }
+
+ bool operator==(decltype(nullptr)) const
+ {
+ return !mInstance;
+ }
+
+ bool operator!=(decltype(nullptr)) const
+ {
+ return !!mInstance;
+ }
+
+ CopyableCtx operator->() const
+ {
+ return CopyableCtx(FindEnv(), mInstance);
+ }
+
+ // Any ref can be cast to an object ref.
+ operator Ref<Object, jobject>() const
+ {
+ return Ref<Object, jobject>(mInstance);
+ }
+
+ // Null checking (e.g. !!ref) using the safe-bool idiom.
+ operator bool_type() const
+ {
+ return mInstance ? &Self::non_null_reference : nullptr;
+ }
+
+ // We don't allow implicit conversion to jobject because that can lead
+ // to easy mistakes such as assigning a temporary LocalRef to a jobject,
+ // and using the jobject after the LocalRef has been freed.
+
+ // We don't allow explicit conversion, to make outside code use Ref::Get.
+ // Using Ref::Get makes it very easy to see which code is using raw JNI
+ // types to make future refactoring easier.
+
+ // operator JNIType() const = delete;
+};
+
+
+// Represents a calling context for JNI methods.
+template<class Cls, typename Type>
+class Context : public Ref<Cls, Type>
+{
+ using Ref = jni::Ref<Cls, Type>;
+
+ static jclass sClassRef; // global reference
+
+protected:
+ JNIEnv* const mEnv;
+
+public:
+ static jclass RawClassRef()
+ {
+ return sClassRef;
+ }
+
+ Context()
+ : Ref(nullptr)
+ , mEnv(Ref::FindEnv())
+ {}
+
+ Context(JNIEnv* env, Type instance)
+ : Ref(instance)
+ , mEnv(env)
+ {}
+
+ jclass ClassRef() const
+ {
+ if (!sClassRef) {
+ const jclass cls = GetClassRef(mEnv, Cls::name);
+ sClassRef = jclass(mEnv->NewGlobalRef(cls));
+ mEnv->DeleteLocalRef(cls);
+ }
+ return sClassRef;
+ }
+
+ JNIEnv* Env() const
+ {
+ return mEnv;
+ }
+
+ bool operator==(const Ref& other) const
+ {
+ // Treat two references of the same object as being the same.
+ return Ref::mInstance == other.mInstance || JNI_FALSE !=
+ mEnv->IsSameObject(Ref::mInstance, other.mInstance);
+ }
+
+ bool operator!=(const Ref& other) const
+ {
+ return !operator==(other);
+ }
+
+ bool operator==(decltype(nullptr)) const
+ {
+ return !Ref::mInstance;
+ }
+
+ bool operator!=(decltype(nullptr)) const
+ {
+ return !!Ref::mInstance;
+ }
+
+ Cls operator->() const
+ {
+ MOZ_ASSERT(Ref::mInstance, "Null jobject");
+ return Cls(*this);
+ }
+};
+
+
+template<class Cls, typename Type = jobject>
+class ObjectBase
+{
+protected:
+ const jni::Context<Cls, Type>& mCtx;
+
+ jclass ClassRef() const { return mCtx.ClassRef(); }
+ JNIEnv* Env() const { return mCtx.Env(); }
+ Type Instance() const { return mCtx.Get(); }
+
+public:
+ using Ref = jni::Ref<Cls, Type>;
+ using Context = jni::Context<Cls, Type>;
+ using LocalRef = jni::LocalRef<Cls>;
+ using GlobalRef = jni::GlobalRef<Cls>;
+ using Param = const Ref&;
+
+ static const CallingThread callingThread = CallingThread::ANY;
+ static const char name[];
+
+ explicit ObjectBase(const Context& ctx) : mCtx(ctx) {}
+
+ Cls* operator->()
+ {
+ return static_cast<Cls*>(this);
+ }
+};
+
+// Binding for a plain jobject.
+class Object : public ObjectBase<Object, jobject>
+{
+public:
+ explicit Object(const Context& ctx) : ObjectBase<Object, jobject>(ctx) {}
+};
+
+// Binding for a built-in object reference other than jobject.
+template<typename T>
+class TypedObject : public ObjectBase<TypedObject<T>, T>
+{
+public:
+ explicit TypedObject(const Context<TypedObject<T>, T>& ctx)
+ : ObjectBase<TypedObject<T>, T>(ctx)
+ {}
+};
+
+
+// Define bindings for built-in types.
+using String = TypedObject<jstring>;
+using Class = TypedObject<jclass>;
+using Throwable = TypedObject<jthrowable>;
+
+using BooleanArray = TypedObject<jbooleanArray>;
+using ByteArray = TypedObject<jbyteArray>;
+using CharArray = TypedObject<jcharArray>;
+using ShortArray = TypedObject<jshortArray>;
+using IntArray = TypedObject<jintArray>;
+using LongArray = TypedObject<jlongArray>;
+using FloatArray = TypedObject<jfloatArray>;
+using DoubleArray = TypedObject<jdoubleArray>;
+using ObjectArray = TypedObject<jobjectArray>;
+
+
+namespace detail {
+
+// See explanation in LocalRef.
+template<class Cls> struct GenericObject { using Type = Object; };
+template<> struct GenericObject<Object>
+{
+ struct Type {
+ using Ref = jni::Ref<Type, jobject>;
+ using Context = jni::Context<Type, jobject>;
+ };
+};
+template<class Cls> struct GenericLocalRef
+{
+ template<class C> struct Type : jni::Object {};
+};
+template<> struct GenericLocalRef<Object>
+{
+ template<class C> using Type = jni::LocalRef<C>;
+};
+
+} // namespace
+
+template<class Cls>
+class LocalRef : public Cls::Context
+{
+ template<class C> friend class LocalRef;
+
+ using Ctx = typename Cls::Context;
+ using Ref = typename Cls::Ref;
+ using JNIType = typename Ref::JNIType;
+
+ // In order to be able to convert LocalRef<Object> to LocalRef<Cls>, we
+ // need constructors and copy assignment operators that take in a
+ // LocalRef<Object> argument. However, if Cls *is* Object, we would have
+ // duplicated constructors and operators with LocalRef<Object> arguments. To
+ // avoid this conflict, we use GenericObject, which is defined as Object for
+ // LocalRef<non-Object> and defined as a dummy class for LocalRef<Object>.
+ using GenericObject = typename detail::GenericObject<Cls>::Type;
+
+ // Similarly, GenericLocalRef is useed to convert LocalRef<Cls> to,
+ // LocalRef<Object>. It's defined as LocalRef<C> for Cls == Object,
+ // and defined as a dummy template class for Cls != Object.
+ template<class C> using GenericLocalRef
+ = typename detail::GenericLocalRef<Cls>::template Type<C>;
+
+ static JNIType NewLocalRef(JNIEnv* env, JNIType obj)
+ {
+ return JNIType(obj ? env->NewLocalRef(obj) : nullptr);
+ }
+
+ LocalRef(JNIEnv* env, JNIType instance) : Ctx(env, instance) {}
+
+ LocalRef& swap(LocalRef& other)
+ {
+ auto instance = other.mInstance;
+ other.mInstance = Ctx::mInstance;
+ Ctx::mInstance = instance;
+ return *this;
+ }
+
+public:
+ // Construct a LocalRef from a raw JNI local reference. Unlike Ref::From,
+ // LocalRef::Adopt returns a LocalRef that will delete the local reference
+ // when going out of scope.
+ static LocalRef Adopt(JNIType instance)
+ {
+ return LocalRef(Ref::FindEnv(), instance);
+ }
+
+ static LocalRef Adopt(JNIEnv* env, JNIType instance)
+ {
+ return LocalRef(env, instance);
+ }
+
+ // Copy constructor.
+ LocalRef(const LocalRef<Cls>& ref)
+ : Ctx(ref.mEnv, NewLocalRef(ref.mEnv, ref.mInstance))
+ {}
+
+ // Move constructor.
+ LocalRef(LocalRef<Cls>&& ref)
+ : Ctx(ref.mEnv, ref.mInstance)
+ {
+ ref.mInstance = nullptr;
+ }
+
+ explicit LocalRef(JNIEnv* env = Ref::FindEnv())
+ : Ctx(env, nullptr)
+ {}
+
+ // Construct a LocalRef from any Ref,
+ // which means creating a new local reference.
+ MOZ_IMPLICIT LocalRef(const Ref& ref)
+ : Ctx(Ref::FindEnv(), nullptr)
+ {
+ Ctx::mInstance = NewLocalRef(Ctx::mEnv, ref.Get());
+ }
+
+ LocalRef(JNIEnv* env, const Ref& ref)
+ : Ctx(env, NewLocalRef(env, ref.Get()))
+ {}
+
+ // Move a LocalRef<Object> into a LocalRef<Cls> without
+ // creating/deleting local references.
+ MOZ_IMPLICIT LocalRef(LocalRef<GenericObject>&& ref)
+ : Ctx(ref.mEnv, JNIType(ref.mInstance))
+ {
+ ref.mInstance = nullptr;
+ }
+
+ template<class C>
+ MOZ_IMPLICIT LocalRef(GenericLocalRef<C>&& ref)
+ : Ctx(ref.mEnv, ref.mInstance)
+ {
+ ref.mInstance = nullptr;
+ }
+
+ // Implicitly converts nullptr to LocalRef.
+ MOZ_IMPLICIT LocalRef(decltype(nullptr))
+ : Ctx(Ref::FindEnv(), nullptr)
+ {}
+
+ ~LocalRef()
+ {
+ if (Ctx::mInstance) {
+ Ctx::mEnv->DeleteLocalRef(Ctx::mInstance);
+ Ctx::mInstance = nullptr;
+ }
+ }
+
+ // Get the raw JNI reference that can be used as a return value.
+ // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
+ typename Ref::JNIType Forget()
+ {
+ const auto obj = Ctx::Get();
+ Ctx::mInstance = nullptr;
+ return obj;
+ }
+
+ LocalRef<Cls>& operator=(LocalRef<Cls> ref)
+ {
+ return swap(ref);
+ }
+
+ LocalRef<Cls>& operator=(const Ref& ref)
+ {
+ LocalRef<Cls> newRef(Ctx::mEnv, ref);
+ return swap(newRef);
+ }
+
+ LocalRef<Cls>& operator=(LocalRef<GenericObject>&& ref)
+ {
+ LocalRef<Cls> newRef(mozilla::Move(ref));
+ return swap(newRef);
+ }
+
+ template<class C>
+ LocalRef<Cls>& operator=(GenericLocalRef<C>&& ref)
+ {
+ LocalRef<Cls> newRef(mozilla::Move(ref));
+ return swap(newRef);
+ }
+
+ LocalRef<Cls>& operator=(decltype(nullptr))
+ {
+ LocalRef<Cls> newRef(Ctx::mEnv, nullptr);
+ return swap(newRef);
+ }
+};
+
+
+template<class Cls>
+class GlobalRef : public Cls::Ref
+{
+ using Ref = typename Cls::Ref;
+ using JNIType = typename Ref::JNIType;
+
+ static JNIType NewGlobalRef(JNIEnv* env, JNIType instance)
+ {
+ return JNIType(instance ? env->NewGlobalRef(instance) : nullptr);
+ }
+
+ GlobalRef& swap(GlobalRef& other)
+ {
+ auto instance = other.mInstance;
+ other.mInstance = Ref::mInstance;
+ Ref::mInstance = instance;
+ return *this;
+ }
+
+public:
+ GlobalRef()
+ : Ref(nullptr)
+ {}
+
+ // Copy constructor
+ GlobalRef(const GlobalRef& ref)
+ : Ref(NewGlobalRef(GetEnvForThread(), ref.mInstance))
+ {}
+
+ // Move constructor
+ GlobalRef(GlobalRef&& ref)
+ : Ref(ref.mInstance)
+ {
+ ref.mInstance = nullptr;
+ }
+
+ MOZ_IMPLICIT GlobalRef(const Ref& ref)
+ : Ref(NewGlobalRef(GetEnvForThread(), ref.Get()))
+ {}
+
+ GlobalRef(JNIEnv* env, const Ref& ref)
+ : Ref(NewGlobalRef(env, ref.Get()))
+ {}
+
+ MOZ_IMPLICIT GlobalRef(const LocalRef<Cls>& ref)
+ : Ref(NewGlobalRef(ref.Env(), ref.Get()))
+ {}
+
+ // Implicitly converts nullptr to GlobalRef.
+ MOZ_IMPLICIT GlobalRef(decltype(nullptr))
+ : Ref(nullptr)
+ {}
+
+ ~GlobalRef()
+ {
+ if (Ref::mInstance) {
+ Clear(GetEnvForThread());
+ }
+ }
+
+ // Get the raw JNI reference that can be used as a return value.
+ // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
+ typename Ref::JNIType Forget()
+ {
+ const auto obj = Ref::Get();
+ Ref::mInstance = nullptr;
+ return obj;
+ }
+
+ void Clear(JNIEnv* env)
+ {
+ if (Ref::mInstance) {
+ env->DeleteGlobalRef(Ref::mInstance);
+ Ref::mInstance = nullptr;
+ }
+ }
+
+ GlobalRef<Cls>& operator=(GlobalRef<Cls> ref)
+ {
+ return swap(ref);
+ }
+
+ GlobalRef<Cls>& operator=(const Ref& ref)
+ {
+ GlobalRef<Cls> newRef(ref);
+ return swap(newRef);
+ }
+
+ GlobalRef<Cls>& operator=(const LocalRef<Cls>& ref)
+ {
+ GlobalRef<Cls> newRef(ref);
+ return swap(newRef);
+ }
+
+ GlobalRef<Cls>& operator=(decltype(nullptr))
+ {
+ GlobalRef<Cls> newRef(nullptr);
+ return swap(newRef);
+ }
+};
+
+
+template<class Cls>
+class DependentRef : public Cls::Ref
+{
+ using Ref = typename Cls::Ref;
+
+public:
+ DependentRef(typename Ref::JNIType instance)
+ : Ref(instance)
+ {}
+
+ DependentRef(const DependentRef& ref)
+ : Ref(ref.Get())
+ {}
+};
+
+
+class StringParam;
+
+template<>
+class TypedObject<jstring> : public ObjectBase<TypedObject<jstring>, jstring>
+{
+ using Base = ObjectBase<TypedObject<jstring>, jstring>;
+
+public:
+ using Param = const StringParam&;
+
+ explicit TypedObject(const Context& ctx) : Base(ctx) {}
+
+ size_t Length() const
+ {
+ const size_t ret = Base::Env()->GetStringLength(Base::Instance());
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ nsString ToString() const
+ {
+ const jchar* const str = Base::Env()->GetStringChars(
+ Base::Instance(), nullptr);
+ const jsize len = Base::Env()->GetStringLength(Base::Instance());
+
+ nsString result(reinterpret_cast<const char16_t*>(str), len);
+ Base::Env()->ReleaseStringChars(Base::Instance(), str);
+ return result;
+ }
+
+ nsCString ToCString() const
+ {
+ return NS_ConvertUTF16toUTF8(ToString());
+ }
+
+ // Convert jstring to a nsString.
+ operator nsString() const
+ {
+ return ToString();
+ }
+
+ // Convert jstring to a nsCString.
+ operator nsCString() const
+ {
+ return ToCString();
+ }
+};
+
+// Define a custom parameter type for String,
+// which accepts both String::Ref and nsAString/nsACString
+class StringParam : public String::Ref
+{
+ using Ref = String::Ref;
+
+private:
+ // Not null if we should delete ref on destruction.
+ JNIEnv* const mEnv;
+
+ static jstring GetString(JNIEnv* env, const nsAString& str)
+ {
+ const jstring result = env->NewString(
+ reinterpret_cast<const jchar*>(str.BeginReading()),
+ str.Length());
+ MOZ_CATCH_JNI_EXCEPTION(env);
+ return result;
+ }
+
+public:
+ MOZ_IMPLICIT StringParam(decltype(nullptr))
+ : Ref(nullptr)
+ , mEnv(nullptr)
+ {}
+
+ MOZ_IMPLICIT StringParam(const Ref& ref)
+ : Ref(ref.Get())
+ , mEnv(nullptr)
+ {}
+
+ MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, str))
+ , mEnv(env)
+ {}
+
+ MOZ_IMPLICIT StringParam(const char16_t* str, JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, nsDependentString(str)))
+ , mEnv(env)
+ {}
+
+ MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, NS_ConvertUTF8toUTF16(str)))
+ , mEnv(env)
+ {}
+
+ MOZ_IMPLICIT StringParam(const char* str, JNIEnv* env = Ref::FindEnv())
+ : Ref(GetString(env, NS_ConvertUTF8toUTF16(str)))
+ , mEnv(env)
+ {}
+
+ StringParam(StringParam&& other)
+ : Ref(other.Get())
+ , mEnv(other.mEnv)
+ {
+ other.mInstance = nullptr;
+ }
+
+ ~StringParam()
+ {
+ if (mEnv && Get()) {
+ mEnv->DeleteLocalRef(Get());
+ }
+ }
+
+ operator String::LocalRef() const
+ {
+ // We can't return our existing ref because the returned
+ // LocalRef could be freed first, so we need a new local ref.
+ return String::LocalRef(mEnv ? mEnv : Ref::FindEnv(), *this);
+ }
+};
+
+
+namespace detail {
+ template<typename T> struct TypeAdapter;
+}
+
+// Ref specialization for arrays.
+template<typename JNIType, class ElementType>
+class ArrayRefBase : public ObjectBase<TypedObject<JNIType>, JNIType>
+{
+ using Base = ObjectBase<TypedObject<JNIType>, JNIType>;
+
+public:
+ explicit ArrayRefBase(const Context<TypedObject<JNIType>, JNIType>& ctx)
+ : Base(ctx)
+ {}
+
+ static typename Base::LocalRef New(const ElementType* data, size_t length) {
+ using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+ static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+ "Size of native type must match size of JNI type");
+ JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
+ auto result =
+ (jenv->*detail::TypeAdapter<ElementType>::NewArray)(length);
+ MOZ_CATCH_JNI_EXCEPTION(jenv);
+ (jenv->*detail::TypeAdapter<ElementType>::SetArray)(
+ result, jsize(0), length,
+ reinterpret_cast<const JNIElemType*>(data));
+ MOZ_CATCH_JNI_EXCEPTION(jenv);
+ return Base::LocalRef::Adopt(jenv, result);
+ }
+
+ size_t Length() const
+ {
+ const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ ElementType GetElement(size_t index) const
+ {
+ using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+ static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+ "Size of native type must match size of JNI type");
+
+ ElementType ret;
+ (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
+ Base::Instance(), jsize(index), 1,
+ reinterpret_cast<JNIElemType*>(&ret));
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ nsTArray<ElementType> GetElements() const
+ {
+ using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
+ static_assert(sizeof(ElementType) == sizeof(JNIElemType),
+ "Size of native type must match size of JNI type");
+
+ const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
+
+ nsTArray<ElementType> array((size_t(len)));
+ array.SetLength(size_t(len));
+ (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
+ Base::Instance(), 0, len,
+ reinterpret_cast<JNIElemType*>(array.Elements()));
+ return array;
+ }
+
+ ElementType operator[](size_t index) const
+ {
+ return GetElement(index);
+ }
+
+ operator nsTArray<ElementType>() const
+ {
+ return GetElements();
+ }
+};
+
+#define DEFINE_PRIMITIVE_ARRAY_REF(JNIType, ElementType) \
+ template<> \
+ class TypedObject<JNIType> : public ArrayRefBase<JNIType, ElementType> \
+ { \
+ public: \
+ explicit TypedObject(const Context& ctx) \
+ : ArrayRefBase<JNIType, ElementType>(ctx) \
+ {} \
+ }
+
+DEFINE_PRIMITIVE_ARRAY_REF(jbooleanArray, bool);
+DEFINE_PRIMITIVE_ARRAY_REF(jbyteArray, int8_t);
+DEFINE_PRIMITIVE_ARRAY_REF(jcharArray, char16_t);
+DEFINE_PRIMITIVE_ARRAY_REF(jshortArray, int16_t);
+DEFINE_PRIMITIVE_ARRAY_REF(jintArray, int32_t);
+DEFINE_PRIMITIVE_ARRAY_REF(jlongArray, int64_t);
+DEFINE_PRIMITIVE_ARRAY_REF(jfloatArray, float);
+DEFINE_PRIMITIVE_ARRAY_REF(jdoubleArray, double);
+
+#undef DEFINE_PRIMITIVE_ARRAY_REF
+
+
+class ByteBuffer : public ObjectBase<ByteBuffer, jobject>
+{
+public:
+ explicit ByteBuffer(const Context& ctx)
+ : ObjectBase<ByteBuffer, jobject>(ctx)
+ {}
+
+ static LocalRef New(void* data, size_t capacity)
+ {
+ JNIEnv* const env = GetEnvForThread();
+ const auto ret = LocalRef::Adopt(
+ env, env->NewDirectByteBuffer(data, jlong(capacity)));
+ MOZ_CATCH_JNI_EXCEPTION(env);
+ return ret;
+ }
+
+ void* Address()
+ {
+ void* const ret = Env()->GetDirectBufferAddress(Instance());
+ MOZ_CATCH_JNI_EXCEPTION(Env());
+ return ret;
+ }
+
+ size_t Capacity()
+ {
+ const size_t ret = size_t(Env()->GetDirectBufferCapacity(Instance()));
+ MOZ_CATCH_JNI_EXCEPTION(Env());
+ return ret;
+ }
+};
+
+
+template<>
+class TypedObject<jobjectArray>
+ : public ObjectBase<TypedObject<jobjectArray>, jobjectArray>
+{
+ using Base = ObjectBase<TypedObject<jobjectArray>, jobjectArray>;
+
+public:
+ explicit TypedObject(const Context& ctx) : Base(ctx) {}
+
+ size_t Length() const
+ {
+ const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ Object::LocalRef GetElement(size_t index) const
+ {
+ auto ret = Object::LocalRef::Adopt(
+ Base::Env(), Base::Env()->GetObjectArrayElement(
+ Base::Instance(), jsize(index)));
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ return ret;
+ }
+
+ nsTArray<Object::LocalRef> GetElements() const
+ {
+ const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
+
+ nsTArray<Object::LocalRef> array((size_t(len)));
+ for (jsize i = 0; i < len; i++) {
+ array.AppendElement(Object::LocalRef::Adopt(
+ Base::Env(), Base::Env()->GetObjectArrayElement(
+ Base::Instance(), i)));
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ }
+ return array;
+ }
+
+ Object::LocalRef operator[](size_t index) const
+ {
+ return GetElement(index);
+ }
+
+ operator nsTArray<Object::LocalRef>() const
+ {
+ return GetElements();
+ }
+
+ void SetElement(size_t index, Object::Param element) const
+ {
+ Base::Env()->SetObjectArrayElement(
+ Base::Instance(), jsize(index), element.Get());
+ MOZ_CATCH_JNI_EXCEPTION(Base::Env());
+ }
+};
+
+
+// Support conversion from LocalRef<T>* to LocalRef<Object>*:
+// LocalRef<Foo> foo;
+// Foo::GetFoo(&foo); // error because parameter type is LocalRef<Object>*.
+// Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
+template<class Cls>
+class ReturnToLocal
+{
+private:
+ LocalRef<Cls>* const localRef;
+ LocalRef<Object> objRef;
+
+public:
+ explicit ReturnToLocal(LocalRef<Cls>* ref) : localRef(ref) {}
+ operator LocalRef<Object>*() { return &objRef; }
+
+ ~ReturnToLocal()
+ {
+ if (objRef) {
+ *localRef = mozilla::Move(objRef);
+ }
+ }
+};
+
+template<class Cls>
+ReturnToLocal<Cls> ReturnTo(LocalRef<Cls>* ref)
+{
+ return ReturnToLocal<Cls>(ref);
+}
+
+
+// Support conversion from GlobalRef<T>* to LocalRef<Object/T>*:
+// GlobalRef<Foo> foo;
+// Foo::GetFoo(&foo); // error because parameter type is LocalRef<Foo>*.
+// Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
+template<class Cls>
+class ReturnToGlobal
+{
+private:
+ GlobalRef<Cls>* const globalRef;
+ LocalRef<Object> objRef;
+ LocalRef<Cls> clsRef;
+
+public:
+ explicit ReturnToGlobal(GlobalRef<Cls>* ref) : globalRef(ref) {}
+ operator LocalRef<Object>*() { return &objRef; }
+ operator LocalRef<Cls>*() { return &clsRef; }
+
+ ~ReturnToGlobal()
+ {
+ if (objRef) {
+ *globalRef = (clsRef = mozilla::Move(objRef));
+ } else if (clsRef) {
+ *globalRef = clsRef;
+ }
+ }
+};
+
+template<class Cls>
+ReturnToGlobal<Cls> ReturnTo(GlobalRef<Cls>* ref)
+{
+ return ReturnToGlobal<Cls>(ref);
+}
+
+} // namespace jni
+} // namespace mozilla
+
+#endif // mozilla_jni_Refs_h__
diff --git a/widget/android/jni/Types.h b/widget/android/jni/Types.h
new file mode 100644
index 000000000..a083d3e50
--- /dev/null
+++ b/widget/android/jni/Types.h
@@ -0,0 +1,140 @@
+#ifndef mozilla_jni_Types_h__
+#define mozilla_jni_Types_h__
+
+#include <jni.h>
+
+#include "mozilla/jni/Refs.h"
+
+namespace mozilla {
+namespace jni {
+namespace detail {
+
+// TypeAdapter specializations are the interfaces between native/C++ types such
+// as int32_t and JNI types such as jint. The template parameter T is the native
+// type, and each TypeAdapter specialization can have the following members:
+//
+// * Call: JNIEnv member pointer for making a method call that returns T.
+// * StaticCall: JNIEnv member pointer for making a static call that returns T.
+// * Get: JNIEnv member pointer for getting a field of type T.
+// * StaticGet: JNIEnv member pointer for getting a static field of type T.
+// * Set: JNIEnv member pointer for setting a field of type T.
+// * StaticGet: JNIEnv member pointer for setting a static field of type T.
+// * ToNative: static function that converts the JNI type to the native type.
+// * FromNative: static function that converts the native type to the JNI type.
+
+template<typename T> struct TypeAdapter;
+
+
+// TypeAdapter<LocalRef<Cls>> applies when jobject is a return value.
+template<class Cls> struct TypeAdapter<LocalRef<Cls>> {
+ using JNIType = typename Cls::Ref::JNIType;
+
+ static constexpr auto Call = &JNIEnv::CallObjectMethodA;
+ static constexpr auto StaticCall = &JNIEnv::CallStaticObjectMethodA;
+ static constexpr auto Get = &JNIEnv::GetObjectField;
+ static constexpr auto StaticGet = &JNIEnv::GetStaticObjectField;
+
+ // Declare instance as jobject because JNI methods return
+ // jobject even if the return value is really jstring, etc.
+ static LocalRef<Cls> ToNative(JNIEnv* env, jobject instance) {
+ return LocalRef<Cls>::Adopt(env, JNIType(instance));
+ }
+
+ static JNIType FromNative(JNIEnv*, LocalRef<Cls>&& instance) {
+ return instance.Forget();
+ }
+};
+
+// clang is picky about function types, including attributes that modify the calling
+// convention, lining up. GCC appears to be somewhat less so.
+#ifdef __clang__
+#define MOZ_JNICALL_ABI JNICALL
+#else
+#define MOZ_JNICALL_ABI
+#endif
+
+template<class Cls> constexpr jobject
+ (JNIEnv::*TypeAdapter<LocalRef<Cls>>::Call)(jobject, jmethodID, jvalue*) MOZ_JNICALL_ABI;
+template<class Cls> constexpr jobject
+ (JNIEnv::*TypeAdapter<LocalRef<Cls>>::StaticCall)(jclass, jmethodID, jvalue*) MOZ_JNICALL_ABI;
+template<class Cls> constexpr jobject
+ (JNIEnv::*TypeAdapter<LocalRef<Cls>>::Get)(jobject, jfieldID);
+template<class Cls> constexpr jobject
+ (JNIEnv::*TypeAdapter<LocalRef<Cls>>::StaticGet)(jclass, jfieldID);
+
+
+// TypeAdapter<Ref<Cls>> applies when jobject is a parameter value.
+template<class Cls, typename T> struct TypeAdapter<Ref<Cls, T>> {
+ using JNIType = typename Ref<Cls, T>::JNIType;
+
+ static constexpr auto Set = &JNIEnv::SetObjectField;
+ static constexpr auto StaticSet = &JNIEnv::SetStaticObjectField;
+
+ static DependentRef<Cls> ToNative(JNIEnv* env, JNIType instance) {
+ return DependentRef<Cls>(instance);
+ }
+
+ static JNIType FromNative(JNIEnv*, const Ref<Cls, T>& instance) {
+ return instance.Get();
+ }
+};
+
+template<class Cls, typename T> constexpr void
+ (JNIEnv::*TypeAdapter<Ref<Cls, T>>::Set)(jobject, jfieldID, jobject);
+template<class Cls, typename T> constexpr void
+ (JNIEnv::*TypeAdapter<Ref<Cls, T>>::StaticSet)(jclass, jfieldID, jobject);
+
+
+// jstring has its own Param type.
+template<> struct TypeAdapter<StringParam>
+ : public TypeAdapter<String::Ref>
+{};
+
+template<class Cls> struct TypeAdapter<const Cls&>
+ : public TypeAdapter<Cls>
+{};
+
+
+#define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName) \
+ \
+ template<> struct TypeAdapter<NativeType> { \
+ using JNI##Type = JNIType; \
+ \
+ static constexpr auto Call = &JNIEnv::Call ## JNIName ## MethodA; \
+ static constexpr auto StaticCall = &JNIEnv::CallStatic ## JNIName ## MethodA; \
+ static constexpr auto Get = &JNIEnv::Get ## JNIName ## Field; \
+ static constexpr auto StaticGet = &JNIEnv::GetStatic ## JNIName ## Field; \
+ static constexpr auto Set = &JNIEnv::Set ## JNIName ## Field; \
+ static constexpr auto StaticSet = &JNIEnv::SetStatic ## JNIName ## Field; \
+ static constexpr auto GetArray = &JNIEnv::Get ## JNIName ## ArrayRegion; \
+ static constexpr auto SetArray = &JNIEnv::Set ## JNIName ## ArrayRegion; \
+ static constexpr auto NewArray = &JNIEnv::New ## JNIName ## Array; \
+ \
+ static JNIType FromNative(JNIEnv*, NativeType val) { \
+ return static_cast<JNIType>(val); \
+ } \
+ static NativeType ToNative(JNIEnv*, JNIType val) { \
+ return static_cast<NativeType>(val); \
+ } \
+ }
+
+
+DEFINE_PRIMITIVE_TYPE_ADAPTER(bool, jboolean, Boolean);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(int8_t, jbyte, Byte);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(char16_t, jchar, Char);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(int16_t, jshort, Short);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(int32_t, jint, Int);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(int64_t, jlong, Long);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(float, jfloat, Float);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(double, jdouble, Double);
+
+#undef DEFINE_PRIMITIVE_TYPE_ADAPTER
+
+} // namespace detail
+
+using namespace detail;
+
+} // namespace jni
+} // namespace mozilla
+
+#endif // mozilla_jni_Types_h__
diff --git a/widget/android/jni/Utils.cpp b/widget/android/jni/Utils.cpp
new file mode 100644
index 000000000..145f7e9ea
--- /dev/null
+++ b/widget/android/jni/Utils.cpp
@@ -0,0 +1,301 @@
+#include "Utils.h"
+#include "Types.h"
+
+#include <android/log.h>
+#include <pthread.h>
+
+#include "mozilla/Assertions.h"
+
+#include "GeneratedJNIWrappers.h"
+#include "nsAppShell.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+namespace mozilla {
+namespace jni {
+
+namespace detail {
+
+#define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName, ABIName) \
+ \
+ constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Call) \
+ (jobject, jmethodID, jvalue*) MOZ_JNICALL_ABI; \
+ constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticCall) \
+ (jclass, jmethodID, jvalue*) MOZ_JNICALL_ABI; \
+ constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Get) \
+ (jobject, jfieldID) ABIName; \
+ constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticGet) \
+ (jclass, jfieldID) ABIName; \
+ constexpr void (JNIEnv::*TypeAdapter<NativeType>::Set) \
+ (jobject, jfieldID, JNIType) ABIName; \
+ constexpr void (JNIEnv::*TypeAdapter<NativeType>::StaticSet) \
+ (jclass, jfieldID, JNIType) ABIName; \
+ constexpr void (JNIEnv::*TypeAdapter<NativeType>::GetArray) \
+ (JNIType ## Array, jsize, jsize, JNIType*)
+
+DEFINE_PRIMITIVE_TYPE_ADAPTER(bool, jboolean, Boolean, /*nothing*/);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(int8_t, jbyte, Byte, /*nothing*/);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(char16_t, jchar, Char, /*nothing*/);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(int16_t, jshort, Short, /*nothing*/);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(int32_t, jint, Int, /*nothing*/);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(int64_t, jlong, Long, /*nothing*/);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(float, jfloat, Float, MOZ_JNICALL_ABI);
+DEFINE_PRIMITIVE_TYPE_ADAPTER(double, jdouble, Double, MOZ_JNICALL_ABI);
+
+#undef DEFINE_PRIMITIVE_TYPE_ADAPTER
+
+} // namespace detail
+
+template<> const char ObjectBase<Object, jobject>::name[] = "java/lang/Object";
+template<> const char ObjectBase<TypedObject<jstring>, jstring>::name[] = "java/lang/String";
+template<> const char ObjectBase<TypedObject<jclass>, jclass>::name[] = "java/lang/Class";
+template<> const char ObjectBase<TypedObject<jthrowable>, jthrowable>::name[] = "java/lang/Throwable";
+template<> const char ObjectBase<TypedObject<jbooleanArray>, jbooleanArray>::name[] = "[Z";
+template<> const char ObjectBase<TypedObject<jbyteArray>, jbyteArray>::name[] = "[B";
+template<> const char ObjectBase<TypedObject<jcharArray>, jcharArray>::name[] = "[C";
+template<> const char ObjectBase<TypedObject<jshortArray>, jshortArray>::name[] = "[S";
+template<> const char ObjectBase<TypedObject<jintArray>, jintArray>::name[] = "[I";
+template<> const char ObjectBase<TypedObject<jlongArray>, jlongArray>::name[] = "[J";
+template<> const char ObjectBase<TypedObject<jfloatArray>, jfloatArray>::name[] = "[F";
+template<> const char ObjectBase<TypedObject<jdoubleArray>, jdoubleArray>::name[] = "[D";
+template<> const char ObjectBase<TypedObject<jobjectArray>, jobjectArray>::name[] = "[Ljava/lang/Object;";
+template<> const char ObjectBase<ByteBuffer, jobject>::name[] = "java/nio/ByteBuffer";
+
+
+JNIEnv* sGeckoThreadEnv;
+
+namespace {
+
+JavaVM* sJavaVM;
+pthread_key_t sThreadEnvKey;
+jclass sOOMErrorClass;
+jobject sClassLoader;
+jmethodID sClassLoaderLoadClass;
+bool sIsFennec;
+
+void UnregisterThreadEnv(void* env)
+{
+ if (!env) {
+ // We were never attached.
+ return;
+ }
+ // The thread may have already been detached. In that case, it's still
+ // okay to call DetachCurrentThread(); it'll simply return an error.
+ // However, we must not access | env | because it may be invalid.
+ MOZ_ASSERT(sJavaVM);
+ sJavaVM->DetachCurrentThread();
+}
+
+} // namespace
+
+void SetGeckoThreadEnv(JNIEnv* aEnv)
+{
+ MOZ_ASSERT(aEnv);
+ MOZ_ASSERT(!sGeckoThreadEnv || sGeckoThreadEnv == aEnv);
+
+ if (!sGeckoThreadEnv
+ && pthread_key_create(&sThreadEnvKey, UnregisterThreadEnv)) {
+ MOZ_CRASH("Failed to initialize required TLS");
+ }
+
+ sGeckoThreadEnv = aEnv;
+ MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, aEnv));
+
+ MOZ_ALWAYS_TRUE(!aEnv->GetJavaVM(&sJavaVM));
+ MOZ_ASSERT(sJavaVM);
+
+ sOOMErrorClass = Class::GlobalRef(Class::LocalRef::Adopt(
+ aEnv->FindClass("java/lang/OutOfMemoryError"))).Forget();
+ aEnv->ExceptionClear();
+
+ sClassLoader = Object::GlobalRef(java::GeckoThread::ClsLoader()).Forget();
+ sClassLoaderLoadClass = aEnv->GetMethodID(
+ Class::LocalRef::Adopt(aEnv->GetObjectClass(sClassLoader)).Get(),
+ "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+ MOZ_ASSERT(sClassLoader && sClassLoaderLoadClass);
+
+ auto geckoAppClass = Class::LocalRef::Adopt(
+ aEnv->FindClass("org/mozilla/gecko/GeckoApp"));
+ aEnv->ExceptionClear();
+ sIsFennec = !!geckoAppClass;
+}
+
+JNIEnv* GetEnvForThread()
+{
+ MOZ_ASSERT(sGeckoThreadEnv);
+
+ JNIEnv* env = static_cast<JNIEnv*>(pthread_getspecific(sThreadEnvKey));
+ if (env) {
+ return env;
+ }
+
+ // We don't have a saved JNIEnv, so try to get one.
+ // AttachCurrentThread() does the same thing as GetEnv() when a thread is
+ // already attached, so we don't have to call GetEnv() at all.
+ if (!sJavaVM->AttachCurrentThread(&env, nullptr)) {
+ MOZ_ASSERT(env);
+ MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, env));
+ return env;
+ }
+
+ MOZ_CRASH("Failed to get JNIEnv for thread");
+ return nullptr; // unreachable
+}
+
+bool ThrowException(JNIEnv *aEnv, const char *aClass,
+ const char *aMessage)
+{
+ MOZ_ASSERT(aEnv, "Invalid thread JNI env");
+
+ Class::LocalRef cls = Class::LocalRef::Adopt(aEnv->FindClass(aClass));
+ MOZ_ASSERT(cls, "Cannot find exception class");
+
+ return !aEnv->ThrowNew(cls.Get(), aMessage);
+}
+
+bool HandleUncaughtException(JNIEnv* aEnv)
+{
+ MOZ_ASSERT(aEnv, "Invalid thread JNI env");
+
+ if (!aEnv->ExceptionCheck()) {
+ return false;
+ }
+
+#ifdef MOZ_CHECK_JNI
+ aEnv->ExceptionDescribe();
+#endif
+
+ Throwable::LocalRef e =
+ Throwable::LocalRef::Adopt(aEnv, aEnv->ExceptionOccurred());
+ MOZ_ASSERT(e);
+ aEnv->ExceptionClear();
+
+ String::LocalRef stack = java::GeckoAppShell::GetExceptionStackTrace(e);
+ if (stack && ReportException(aEnv, e.Get(), stack.Get())) {
+ return true;
+ }
+
+ aEnv->ExceptionClear();
+ java::GeckoAppShell::HandleUncaughtException(e);
+
+ if (NS_WARN_IF(aEnv->ExceptionCheck())) {
+ aEnv->ExceptionDescribe();
+ aEnv->ExceptionClear();
+ }
+
+ return true;
+}
+
+bool ReportException(JNIEnv* aEnv, jthrowable aExc, jstring aStack)
+{
+ bool result = true;
+
+#ifdef MOZ_CRASHREPORTER
+ result &= NS_SUCCEEDED(CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("JavaStackTrace"),
+ String::Ref::From(aStack)->ToCString()));
+#endif // MOZ_CRASHREPORTER
+
+ if (sOOMErrorClass && aEnv->IsInstanceOf(aExc, sOOMErrorClass)) {
+ NS_ABORT_OOM(0); // Unknown OOM size
+ }
+ return result;
+}
+
+namespace {
+
+jclass sJNIObjectClass;
+jfieldID sJNIObjectHandleField;
+
+bool EnsureJNIObject(JNIEnv* env, jobject instance) {
+ if (!sJNIObjectClass) {
+ sJNIObjectClass = Class::GlobalRef(Class::LocalRef::Adopt(GetClassRef(
+ env, "org/mozilla/gecko/mozglue/JNIObject"))).Forget();
+
+ sJNIObjectHandleField = env->GetFieldID(
+ sJNIObjectClass, "mHandle", "J");
+ }
+
+ MOZ_ASSERT(env->IsInstanceOf(instance, sJNIObjectClass));
+ return true;
+}
+
+} // namespace
+
+uintptr_t GetNativeHandle(JNIEnv* env, jobject instance)
+{
+ if (!EnsureJNIObject(env, instance)) {
+ return 0;
+ }
+
+ return static_cast<uintptr_t>(
+ env->GetLongField(instance, sJNIObjectHandleField));
+}
+
+void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle)
+{
+ if (!EnsureJNIObject(env, instance)) {
+ return;
+ }
+
+ env->SetLongField(instance, sJNIObjectHandleField,
+ static_cast<jlong>(handle));
+}
+
+jclass GetClassRef(JNIEnv* aEnv, const char* aClassName)
+{
+ // First try the default class loader.
+ auto classRef = Class::LocalRef::Adopt(aEnv, aEnv->FindClass(aClassName));
+
+ if (!classRef && sClassLoader) {
+ // If the default class loader failed but we have an app class loader, try that.
+ // Clear the pending exception from failed FindClass call above.
+ aEnv->ExceptionClear();
+ classRef = Class::LocalRef::Adopt(aEnv, jclass(
+ aEnv->CallObjectMethod(sClassLoader, sClassLoaderLoadClass,
+ StringParam(aClassName, aEnv).Get())));
+ }
+
+ if (classRef) {
+ return classRef.Forget();
+ }
+
+ __android_log_print(
+ ANDROID_LOG_ERROR, "Gecko",
+ ">>> FATAL JNI ERROR! FindClass(className=\"%s\") failed. "
+ "Did ProGuard optimize away something it shouldn't have?",
+ aClassName);
+ aEnv->ExceptionDescribe();
+ MOZ_CRASH("Cannot find JNI class");
+ return nullptr;
+}
+
+void DispatchToGeckoThread(UniquePtr<AbstractCall>&& aCall)
+{
+ class AbstractCallEvent : public nsAppShell::Event
+ {
+ UniquePtr<AbstractCall> mCall;
+
+ public:
+ AbstractCallEvent(UniquePtr<AbstractCall>&& aCall)
+ : mCall(Move(aCall))
+ {}
+
+ void Run() override
+ {
+ (*mCall)();
+ }
+ };
+
+ nsAppShell::PostEvent(MakeUnique<AbstractCallEvent>(Move(aCall)));
+}
+
+bool IsFennec()
+{
+ return sIsFennec;
+}
+
+} // jni
+} // mozilla
diff --git a/widget/android/jni/Utils.h b/widget/android/jni/Utils.h
new file mode 100644
index 000000000..38e0b6b0c
--- /dev/null
+++ b/widget/android/jni/Utils.h
@@ -0,0 +1,147 @@
+#ifndef mozilla_jni_Utils_h__
+#define mozilla_jni_Utils_h__
+
+#include <jni.h>
+
+#include "mozilla/UniquePtr.h"
+
+#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#define MOZ_CHECK_JNI
+#endif
+
+#ifdef MOZ_CHECK_JNI
+#include <pthread.h>
+#include "mozilla/Assertions.h"
+#include "APKOpen.h"
+#include "MainThreadUtils.h"
+#endif
+
+namespace mozilla {
+namespace jni {
+
+// How exception during a JNI call should be treated.
+enum class ExceptionMode
+{
+ // Abort on unhandled excepion (default).
+ ABORT,
+ // Ignore the exception and return to caller.
+ IGNORE,
+ // Catch any exception and return a nsresult.
+ NSRESULT,
+};
+
+// Thread that a particular JNI call is allowed on.
+enum class CallingThread
+{
+ // Can be called from any thread (default).
+ ANY,
+ // Can be called from the Gecko thread.
+ GECKO,
+ // Can be called from the Java UI thread.
+ UI,
+};
+
+// If and where a JNI call will be dispatched.
+enum class DispatchTarget
+{
+ // Call happens synchronously on the calling thread (default).
+ CURRENT,
+ // Call happens synchronously on the calling thread, but the call is
+ // wrapped in a function object and is passed thru UsesNativeCallProxy.
+ // Method must return void.
+ PROXY,
+ // Call is dispatched asynchronously on the Gecko thread. Method must
+ // return void.
+ GECKO,
+};
+
+
+extern JNIEnv* sGeckoThreadEnv;
+
+inline bool IsAvailable()
+{
+ return !!sGeckoThreadEnv;
+}
+
+inline JNIEnv* GetGeckoThreadEnv()
+{
+#ifdef MOZ_CHECK_JNI
+ MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Must be on Gecko thread");
+ MOZ_RELEASE_ASSERT(sGeckoThreadEnv, "Must have a JNIEnv");
+#endif
+ return sGeckoThreadEnv;
+}
+
+void SetGeckoThreadEnv(JNIEnv* aEnv);
+
+JNIEnv* GetEnvForThread();
+
+#ifdef MOZ_CHECK_JNI
+#define MOZ_ASSERT_JNI_THREAD(thread) \
+ do { \
+ if ((thread) == mozilla::jni::CallingThread::GECKO) { \
+ MOZ_RELEASE_ASSERT(::NS_IsMainThread()); \
+ } else if ((thread) == mozilla::jni::CallingThread::UI) { \
+ const bool isOnUiThread = ::pthread_equal(::pthread_self(), \
+ ::getJavaUiThread()); \
+ MOZ_RELEASE_ASSERT(isOnUiThread); \
+ } \
+ } while (0)
+#else
+#define MOZ_ASSERT_JNI_THREAD(thread) do {} while (0)
+#endif
+
+bool ThrowException(JNIEnv *aEnv, const char *aClass,
+ const char *aMessage);
+
+inline bool ThrowException(JNIEnv *aEnv, const char *aMessage)
+{
+ return ThrowException(aEnv, "java/lang/Exception", aMessage);
+}
+
+inline bool ThrowException(const char *aClass, const char *aMessage)
+{
+ return ThrowException(GetEnvForThread(), aClass, aMessage);
+}
+
+inline bool ThrowException(const char *aMessage)
+{
+ return ThrowException(GetEnvForThread(), aMessage);
+}
+
+bool HandleUncaughtException(JNIEnv* aEnv);
+
+bool ReportException(JNIEnv* aEnv, jthrowable aExc, jstring aStack);
+
+#define MOZ_CATCH_JNI_EXCEPTION(env) \
+ do { \
+ if (mozilla::jni::HandleUncaughtException((env))) { \
+ MOZ_CRASH("JNI exception"); \
+ } \
+ } while (0)
+
+
+uintptr_t GetNativeHandle(JNIEnv* env, jobject instance);
+
+void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle);
+
+jclass GetClassRef(JNIEnv* aEnv, const char* aClassName);
+
+struct AbstractCall
+{
+ virtual ~AbstractCall() {}
+ virtual void operator()() = 0;
+};
+
+void DispatchToGeckoThread(UniquePtr<AbstractCall>&& aCall);
+
+/**
+ * Returns whether Gecko is running in a Fennec environment, as determined by
+ * the presence of the GeckoApp class.
+ */
+bool IsFennec();
+
+} // jni
+} // mozilla
+
+#endif // mozilla_jni_Utils_h__
diff --git a/widget/android/jni/moz.build b/widget/android/jni/moz.build
new file mode 100644
index 000000000..31d7d32e6
--- /dev/null
+++ b/widget/android/jni/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.jni += [
+ 'Accessors.h',
+ 'Natives.h',
+ 'Refs.h',
+ 'Types.h',
+ 'Utils.h',
+]
+
+UNIFIED_SOURCES += [
+ 'Utils.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '/widget',
+ '/widget/android',
+]