--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -23,29 +23,27 @@
#include "nsIAndroidBridge.h"
#include "nsIMobileMessageCallback.h"
#include "mozilla/Likely.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Types.h"
+#include "mozilla/jni/Utils.h"
// Some debug #defines
// #define DEBUG_ANDROID_EVENTS
// #define DEBUG_ANDROID_WIDGET
class nsWindow;
class nsIDOMMozSmsMessage;
class nsIObserver;
class Task;
-/* See the comment in AndroidBridge about this function before using it */
-extern "C" MOZ_EXPORT JNIEnv * GetJNIForThread();
-
extern bool mozilla_AndroidBridge_SetMainThread(pthread_t);
namespace base {
class Thread;
} // end namespace base
typedef void* EGLSurface;
@@ -161,39 +159,26 @@ public:
}
static bool HasEnv() {
return sBridge && sBridge->mJNIEnv;
}
static bool ThrowException(JNIEnv *aEnv, const char *aClass,
const char *aMessage) {
- MOZ_ASSERT(aEnv, "Invalid thread JNI env");
- jclass cls = aEnv->FindClass(aClass);
- MOZ_ASSERT(cls, "Cannot find exception class");
- bool ret = !aEnv->ThrowNew(cls, aMessage);
- aEnv->DeleteLocalRef(cls);
- return ret;
+
+ return jni::ThrowException(aEnv, aClass, aMessage);
}
static bool ThrowException(JNIEnv *aEnv, const char *aMessage) {
- return ThrowException(aEnv, "java/lang/Exception", aMessage);
+ return jni::ThrowException(aEnv, aMessage);
}
static void HandleUncaughtException(JNIEnv *aEnv) {
- MOZ_ASSERT(aEnv);
- if (!aEnv->ExceptionCheck()) {
- return;
- }
- jthrowable e = aEnv->ExceptionOccurred();
- MOZ_ASSERT(e);
- aEnv->ExceptionClear();
- mozilla::widget::android::GeckoAppShell::HandleUncaughtException(nullptr, e);
- // Should be dead by now...
- MOZ_CRASH("Failed to handle uncaught exception");
+ jni::HandleUncaughtException(aEnv);
}
// The bridge needs to be constructed via ConstructBridge first,
// and then once the Gecko main thread is spun up (Gecko side),
// SetMainThread should be called which will create the JNIEnv for
// us to use. toolkit/xre/nsAndroidStartup.cpp calls
// SetMainThread.
bool SetMainThread(pthread_t thr);
@@ -552,17 +537,17 @@ public:
}
JNIEnv* GetEnv() {
return mJNIEnv;
}
bool CheckForException() {
if (mJNIEnv->ExceptionCheck()) {
- AndroidBridge::HandleUncaughtException(mJNIEnv);
+ jni::HandleUncaughtException(mJNIEnv);
return true;
}
return false;
}
// Note! Calling Purge makes all previous local refs created in
// the AutoLocalJNIFrame's scope INVALID; be sure that you locked down
// any local refs that you need to keep around in global refs!
new file mode 100644
--- /dev/null
+++ b/widget/android/jni/Accessors.h
@@ -0,0 +1,287 @@
+#ifndef mozilla_jni_Accessors_h__
+#define mozilla_jni_Accessors_h__
+
+#include <jni.h>
+
+#include "mozilla/Attributes.h"
+#include "mozilla/jni/Refs.h"
+#include "mozilla/jni/Types.h"
+#include "AndroidBridge.h"
+
+namespace mozilla {
+namespace jni{
+
+namespace {
+
+// 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;
+};
+
+}
+
+// Base class for Method<>, Field<>, and Constructor<>.
+class Accessor {
+private:
+ template<class Cls>
+ static void EnsureClassRef(JNIEnv* env)
+ {
+ if (!Cls::sClassRef) {
+ MOZ_ALWAYS_TRUE(Cls::sClassRef =
+ AndroidBridge::GetClassGlobalRef(env, Cls::name));
+ }
+ }
+
+ static void GetNsresult(JNIEnv* env, nsresult* rv)
+ {
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ *rv = NS_ERROR_FAILURE;
+ } else {
+ *rv = NS_OK;
+ }
+ }
+
+protected:
+ // Called before making a JNIEnv call.
+ template<class Traits>
+ static JNIEnv* BeginAccess()
+ {
+ JNIEnv* const env = Traits::isMultithreaded
+ ? GetJNIForThread() : AndroidBridge::GetJNIEnv();
+
+ EnsureClassRef<class Traits::Owner>(env);
+ return env;
+ }
+
+ // Called after making a JNIEnv call.
+ template<class Traits>
+ static void EndAccess(JNIEnv* env, nsresult* rv)
+ {
+ if (Traits::exceptionMode == ExceptionMode::ABORT) {
+ return HandleUncaughtException(env);
+
+ } else if (Traits::exceptionMode == ExceptionMode::NSRESULT) {
+ return GetNsresult(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 class Traits::Owner Owner;
+
+protected:
+ static jmethodID sID;
+
+ static JNIEnv* BeginAccess()
+ {
+ JNIEnv* const env = Base::BeginAccess<Traits>();
+
+ if (sID) {
+ return env;
+ }
+
+ if (Traits::isStatic) {
+ MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticMethodID(
+ env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
+ } else {
+ MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetMethodID(
+ env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
+ }
+ return env;
+ }
+
+ static void EndAccess(JNIEnv* env, nsresult* rv)
+ {
+ return Base::EndAccess<Traits>(env, rv);
+ }
+
+public:
+ template<typename... Args>
+ static ReturnType Call(const Owner* cls, nsresult* rv, const Args&... args)
+ {
+ JNIEnv* const env = BeginAccess();
+
+ jvalue jargs[] = {
+ Value(TypeAdapter<Args>::FromNative(env, args)).val ...
+ };
+
+ auto result = TypeAdapter<ReturnType>::ToNative(env,
+ Traits::isStatic ?
+ (env->*TypeAdapter<ReturnType>::StaticCall)(
+ Owner::sClassRef, sID, jargs) :
+ (env->*TypeAdapter<ReturnType>::Call)(
+ cls->mInstance, sID, jargs));
+
+ EndAccess(env, 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 Owner;
+
+public:
+ template<typename... Args>
+ static void Call(const Owner* cls, nsresult* rv,
+ const Args&... args) MOZ_OVERRIDE
+ {
+ JNIEnv* const env = Base::BeginAccess();
+
+ jvalue jargs[] = {
+ Value(TypeAdapter<Args>::FromNative(env, args)).val ...
+ };
+
+ if (Traits::isStatic) {
+ env->CallStaticVoidMethodA(Owner::sClassRef, Base::sID, jargs);
+ } else {
+ env->CallVoidMethodA(cls->mInstance, Base::sID, jargs);
+ }
+
+ Base::EndAccess(env, 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 class Traits::Owner Owner;
+ typedef typename Traits::ReturnType ReturnType;
+ typedef Method<Traits, ReturnType> Base;
+
+public:
+ template<typename... Args>
+ static ReturnType Call(const Owner* cls, nsresult* rv,
+ const Args&... args) MOZ_OVERRIDE
+ {
+ JNIEnv* const env = Base::BeginAccess();
+
+ jvalue jargs[] = {
+ Value(TypeAdapter<Args>::FromNative(env, args)).val ...
+ };
+
+ auto result = TypeAdapter<ReturnType>::ToNative(
+ env, env->NewObjectA(Owner::sClassRef, Base::sID, jargs));
+
+ Base::EndAccess(env, 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 class Traits::Owner Owner;
+ typedef typename Traits::ReturnType GetterType;
+ typedef typename Traits::SetterType SetterType;
+
+ template<typename T> struct RemoveRef { typedef T Type; };
+ template<typename T> struct RemoveRef<const T&> { typedef T Type; };
+
+ // Setter type without any const/& added
+ typedef typename RemoveRef<SetterType>::Type SetterBaseType;
+
+private:
+
+ static jfieldID sID;
+
+ static JNIEnv* BeginAccess()
+ {
+ JNIEnv* const env = Base::BeginAccess<Traits>();
+
+ if (sID) {
+ return env;
+ }
+
+ if (Traits::isStatic) {
+ MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticFieldID(
+ env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
+ } else {
+ MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetFieldID(
+ env, Traits::Owner::sClassRef, Traits::name, Traits::signature));
+ }
+ return env;
+ }
+
+ static void EndAccess(JNIEnv* env, nsresult* rv)
+ {
+ return Base::EndAccess<Traits>(env, rv);
+ }
+
+public:
+ static GetterType Get(const Owner* cls, nsresult* rv)
+ {
+ JNIEnv* const env = BeginAccess();
+
+ auto result = TypeAdapter<GetterType>::ToNative(
+ env, Traits::isStatic ?
+
+ (env->*TypeAdapter<GetterType>::StaticGet)
+ (Owner::sClassRef, sID) :
+
+ (env->*TypeAdapter<GetterType>::Get)
+ (cls->mInstance, sID));
+
+ EndAccess(env, rv);
+ return result;
+ }
+
+ static void Set(const Owner* cls, nsresult* rv, SetterType val)
+ {
+ JNIEnv* const env = BeginAccess();
+
+ if (Traits::isStatic) {
+ (env->*TypeAdapter<SetterBaseType>::StaticSet)(
+ Owner::sClassRef, sID,
+ TypeAdapter<SetterBaseType>::FromNative(env, val));
+ } else {
+ (env->*TypeAdapter<SetterBaseType>::Set)(
+ cls->mInstance, sID,
+ TypeAdapter<SetterBaseType>::FromNative(env, val));
+ }
+
+ EndAccess(env, 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> jclass Class<C>::sClassRef;
+
+} // namespace jni
+} // namespace mozilla
+
+#endif // mozilla_jni_Accessors_h__
new file mode 100644
--- /dev/null
+++ b/widget/android/jni/Refs.h
@@ -0,0 +1,644 @@
+#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"
+
+namespace mozilla {
+namespace jni {
+
+class Accessor;
+template<class T> class Constructor;
+template<class T> class Field;
+template<class T, typename R> class Method;
+
+// Wrapped object reference (e.g. jobject, jclass, etc...)
+template<class Cls> class Ref;
+// Wrapped local reference that inherits from Ref.
+template<class Cls> class LocalRef;
+// Wrapped global reference that inherits from Ref.
+template<class Cls> class GlobalRef;
+
+// Type used for a reference parameter. Default is a wrapped object
+// reference, but Param can be specialized to define custom behavior,
+// e.g. a StringParam class that automatically converts nsAString& and
+// nsACString& to a jstring.
+template<class Cls> struct Param { typedef Ref<Cls> Type; };
+
+
+// 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,
+};
+
+
+// Base class for all JNI binding classes.
+// Templated so that we have one sClassRef for each class.
+template<class Cls>
+class Class
+{
+ friend class Accessor;
+ template<class T> friend class Constructor;
+ template<class T> friend class Field;
+ template<class T, typename R> friend class Method;
+
+private:
+ static jclass sClassRef; // global reference
+
+protected:
+ jobject mInstance; // local or global reference
+
+ Class(jobject instance) : mInstance(instance) {}
+};
+
+
+// Binding for a plain jobject.
+class Object : public Class<Object>
+{
+protected:
+ Object(jobject instance) : Class(instance) {}
+
+public:
+ typedef jni::Ref<Object> Ref;
+ typedef jni::LocalRef<Object> LocalRef;
+ typedef jni::GlobalRef<Object> GlobalRef;
+ typedef const typename jni::Param<Object>::Type& Param;
+};
+
+
+// Binding for a built-in object reference other than jobject.
+template<typename T>
+class TypedObject : public Object
+{
+ typedef TypedObject<T> Self;
+
+protected:
+ TypedObject(jobject instance) : Object(instance) {}
+
+public:
+ typedef jni::Ref<Self> Ref;
+ typedef jni::LocalRef<Self> LocalRef;
+ typedef jni::GlobalRef<Self> GlobalRef;
+ typedef const typename jni::Param<Self>::Type& Param;
+};
+
+// Define bindings for built-in types.
+typedef TypedObject<jstring> String;
+typedef TypedObject<jclass> ClassObject;
+typedef TypedObject<jthrowable> Throwable;
+
+typedef TypedObject<jbooleanArray> BooleanArray;
+typedef TypedObject<jbyteArray> ByteArray;
+typedef TypedObject<jcharArray> CharArray;
+typedef TypedObject<jshortArray> ShortArray;
+typedef TypedObject<jintArray> IntArray;
+typedef TypedObject<jlongArray> LongArray;
+typedef TypedObject<jfloatArray> FloatArray;
+typedef TypedObject<jdoubleArray> DoubleArray;
+typedef TypedObject<jobjectArray> ObjectArray;
+
+template<> struct Param<String> { class Type; };
+
+
+// Base class for Ref and its specializations.
+template<class Cls, typename JNIType>
+class RefBase : protected Cls
+{
+ typedef RefBase<Cls, JNIType> Self;
+ typedef void (Self::*bool_type)() const;
+ void non_null_reference() const {}
+
+protected:
+ RefBase(jobject instance) : Cls(instance) {}
+
+public:
+ // Construct a Ref form a raw JNI reference.
+ static Ref<Cls> From(JNIType obj)
+ {
+ return Ref<Cls>(static_cast<jobject>(obj));
+ }
+
+ // Get the raw JNI reference.
+ JNIType Get() const
+ {
+ return static_cast<JNIType>(Cls::mInstance);
+ }
+
+ bool operator==(const RefBase& other) const
+ {
+ // Treat two references of the same object as being the same.
+ return Cls::mInstance == other.mInstance &&
+ GetJNIForThread()->IsSameObject(
+ Cls::mInstance, other.mInstance) != JNI_FALSE;
+ }
+
+ bool operator!=(const RefBase& other) const
+ {
+ return !operator==(other);
+ }
+
+ bool operator==(decltype(nullptr)) const
+ {
+ return !Cls::mInstance;
+ }
+
+ bool operator!=(decltype(nullptr)) const
+ {
+ return !!Cls::mInstance;
+ }
+
+ Cls* operator->()
+ {
+ MOZ_ASSERT(Cls::mInstance);
+ return this;
+ }
+
+ const Cls* operator->() const
+ {
+ MOZ_ASSERT(Cls::mInstance);
+ return this;
+ }
+
+ // Any ref can be cast to an object ref.
+ operator Ref<Object>() const;
+
+ // Null checking (e.g. !!ref) using the safe-bool idiom.
+ operator bool_type() const
+ {
+ return Cls::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;
+};
+
+
+// Wrapped object reference (e.g. jobject, jclass, etc...)
+template<class Cls>
+class Ref : public RefBase<Cls, jobject>
+{
+ template<class C, typename T> friend class RefBase;
+ friend class jni::LocalRef<Cls>;
+ friend class jni::GlobalRef<Cls>;
+
+ typedef RefBase<Cls, jobject> Base;
+
+protected:
+ // 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.
+ Ref(jobject instance) : Base(instance) {}
+
+ // Protected 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& ref) : Base(ref.mInstance) {}
+
+public:
+ MOZ_IMPLICIT Ref(decltype(nullptr)) : Base(nullptr) {}
+};
+
+
+template<class Cls, typename JNIType>
+RefBase<Cls, JNIType>::operator Ref<Object>() const
+{
+ return Ref<Object>(Cls::mInstance);
+}
+
+
+template<typename T>
+class Ref<TypedObject<T>>
+ : public RefBase<TypedObject<T>, T>
+{
+ friend class RefBase<TypedObject<T>, T>;
+ friend class jni::LocalRef<TypedObject<T>>;
+ friend class jni::GlobalRef<TypedObject<T>>;
+
+ typedef RefBase<TypedObject<T>, T> Base;
+
+protected:
+ Ref(jobject instance) : Base(instance) {}
+
+ Ref(const Ref& ref) : Base(ref.mInstance) {}
+
+public:
+ MOZ_IMPLICIT Ref(decltype(nullptr)) : Base(nullptr) {}
+};
+
+
+namespace {
+
+// See explanation in LocalRef.
+template<class Cls> struct GenericObject { typedef Object Type; };
+template<> struct GenericObject<Object> { typedef struct {} Type; };
+
+} // namespace
+
+template<class Cls>
+class LocalRef : public Ref<Cls>
+{
+ template<class C> friend class LocalRef;
+
+private:
+ // 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>.
+ typedef typename GenericObject<Cls>::Type GenericObject;
+
+ JNIEnv* const mEnv;
+
+ LocalRef(JNIEnv* env, jobject instance)
+ : Ref<Cls>(instance)
+ , mEnv(env)
+ {}
+
+ LocalRef& swap(LocalRef& other)
+ {
+ auto instance = other.mInstance;
+ other.mInstance = Ref<Cls>::mInstance;
+ Ref<Cls>::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(jobject instance)
+ {
+ return LocalRef(GetJNIForThread(), instance);
+ }
+
+ static LocalRef Adopt(JNIEnv* env, jobject instance)
+ {
+ return LocalRef(env, instance);
+ }
+
+ // Copy constructor.
+ LocalRef(const LocalRef<Cls>& ref)
+ : Ref<Cls>(ref.mEnv->NewLocalRef(ref.mInstance))
+ , mEnv(ref.mEnv)
+ {}
+
+ // Move constructor.
+ LocalRef(LocalRef<Cls>&& ref)
+ : Ref<Cls>(ref.mInstance)
+ , mEnv(ref.mEnv)
+ {
+ ref.mInstance = nullptr;
+ }
+
+ explicit LocalRef(JNIEnv* env = GetJNIForThread())
+ : Ref<Cls>(nullptr)
+ , mEnv(env)
+ {}
+
+ // Construct a LocalRef from any Ref,
+ // which means creating a new local reference.
+ MOZ_IMPLICIT LocalRef(const Ref<Cls>& ref)
+ : Ref<Cls>(nullptr)
+ , mEnv(GetJNIForThread())
+ {
+ Ref<Cls>::mInstance = mEnv->NewLocalRef(ref.mInstance);
+ }
+
+ // Move a LocalRef<Object> into a LocalRef<Cls> without
+ // creating/deleting local references.
+ MOZ_IMPLICIT LocalRef(LocalRef<GenericObject>&& ref)
+ : Ref<Cls>(ref.mInstance)
+ , mEnv(ref.mEnv)
+ {
+ ref.mInstance = nullptr;
+ }
+
+ // Implicitly converts nullptr to LocalRef.
+ MOZ_IMPLICIT LocalRef(decltype(nullptr))
+ : Ref<Cls>(nullptr)
+ , mEnv(GetJNIForThread())
+ {}
+
+ ~LocalRef()
+ {
+ if (Ref<Cls>::mInstance) {
+ mEnv->DeleteLocalRef(Ref<Cls>::mInstance);
+ Ref<Cls>::mInstance = nullptr;
+ }
+ }
+
+ // Get the JNIEnv* associated with this local reference.
+ JNIEnv* Env() const
+ {
+ return mEnv;
+ }
+
+ // 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.
+ auto Forget() -> decltype(Ref<Cls>(nullptr).Get())
+ {
+ const auto obj = Ref<Cls>::Get();
+ Ref<Cls>::mInstance = nullptr;
+ return obj;
+ }
+
+ LocalRef<Cls>& operator=(LocalRef<Cls> ref)
+ {
+ return swap(ref);
+ }
+
+ LocalRef<Cls>& operator=(const Ref<Cls>& ref)
+ {
+ LocalRef<Cls> newRef(mEnv, ref.mInstance);
+ return swap(newRef);
+ }
+
+ LocalRef<Cls>& operator=(LocalRef<GenericObject>&& ref)
+ {
+ LocalRef<Cls> newRef(mozilla::Forward<LocalRef<GenericObject>>(ref));
+ return swap(newRef);
+ }
+
+ LocalRef<Cls>& operator=(decltype(nullptr))
+ {
+ LocalRef<Cls> newRef(mEnv, nullptr);
+ return swap(newRef);
+ }
+};
+
+
+template<class Cls>
+class GlobalRef : public Ref<Cls>
+{
+private:
+ static jobject NewGlobalRef(JNIEnv* env, jobject instance)
+ {
+ if (!instance) {
+ return nullptr;
+ }
+ if (!env) {
+ env = GetJNIForThread();
+ }
+ return env->NewGlobalRef(instance);
+ }
+
+ GlobalRef& swap(GlobalRef& other)
+ {
+ auto instance = other.mInstance;
+ other.mInstance = Ref<Cls>::mInstance;
+ Ref<Cls>::mInstance = instance;
+ return *this;
+ }
+
+public:
+ GlobalRef()
+ : Ref<Cls>(nullptr)
+ {}
+
+ // Copy constructor
+ GlobalRef(const GlobalRef& ref)
+ : Ref<Cls>(ref.mInstance)
+ {}
+
+ // Move constructor
+ GlobalRef(GlobalRef&& ref)
+ : Ref<Cls>(ref.mInstance)
+ {
+ ref.mInstance = nullptr;
+ }
+
+ MOZ_IMPLICIT GlobalRef(const Ref<Cls>& ref)
+ : Ref<Cls>(NewGlobalRef(nullptr, ref.mInstance))
+ {}
+
+ GlobalRef(JNIEnv* env, const Ref<Cls>& ref)
+ : Ref<Cls>(NewGlobalRef(env, ref.mInstance))
+ {}
+
+ // Implicitly converts nullptr to GlobalRef.
+ MOZ_IMPLICIT GlobalRef(decltype(nullptr))
+ : Ref<Cls>(nullptr)
+ {}
+
+ ~GlobalRef()
+ {
+ if (Ref<Cls>::mInstance) {
+ JNIEnv* const env = GetJNIForThread();
+ env->DeleteGlobalRef(Ref<Cls>::mInstance);
+ Ref<Cls>::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.
+ auto Forget() -> decltype(Ref<Cls>(nullptr).Get())
+ {
+ const auto obj = Ref<Cls>::Get();
+ Ref<Cls>::mInstance = nullptr;
+ return obj;
+ }
+
+ GlobalRef<Cls>& operator=(GlobalRef<Cls> ref)
+ {
+ return swap(ref);
+ }
+
+ GlobalRef<Cls>& operator=(const Ref<Cls>& ref)
+ {
+ GlobalRef<Cls> newRef(ref);
+ return swap(newRef);
+ }
+
+ GlobalRef<Cls>& operator=(decltype(nullptr))
+ {
+ GlobalRef<Cls> newRef(nullptr);
+ return swap(newRef);
+ }
+};
+
+
+// Ref specialization for jstring.
+template<>
+class Ref<String> : public RefBase<String, jstring>
+{
+ friend class RefBase<String, jstring>;
+ friend class jni::LocalRef<String>;
+ friend class jni::GlobalRef<String>;
+
+ typedef RefBase<TypedObject<jstring>, jstring> Base;
+
+protected:
+ Ref(jobject instance) : Base(instance) {}
+
+ Ref(const Ref& ref) : Base(ref.mInstance) {}
+
+public:
+ MOZ_IMPLICIT Ref(decltype(nullptr)) : Base(nullptr) {}
+
+ // Get the length of the jstring.
+ size_t Length() const
+ {
+ JNIEnv* const env = GetJNIForThread();
+ return env->GetStringLength(Get());
+ }
+
+ // Convert jstring to a nsString.
+ operator nsString() const
+ {
+ MOZ_ASSERT(Object::mInstance);
+
+ JNIEnv* const env = GetJNIForThread();
+ const jchar* const str = env->GetStringChars(Get(), nullptr);
+ const jsize len = env->GetStringLength(Get());
+
+ nsString result(reinterpret_cast<const char16_t*>(str), len);
+ env->ReleaseStringChars(Get(), str);
+ return result;
+ }
+
+ // Convert jstring to a nsCString.
+ operator nsCString() const
+ {
+ return NS_ConvertUTF16toUTF8(operator nsString());
+ }
+};
+
+
+// Define a custom parameter type for String,
+// which accepts both String::Ref and nsAString/nsACString
+class Param<String>::Type : public Ref<String>
+{
+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());
+ HandleUncaughtException(env);
+ return result;
+ }
+
+public:
+ MOZ_IMPLICIT Type(const String::Ref& ref)
+ : Ref<String>(ref.Get())
+ , mEnv(nullptr)
+ {}
+
+ MOZ_IMPLICIT Type(const nsAString& str, JNIEnv* env = GetJNIForThread())
+ : Ref<String>(GetString(env, str))
+ , mEnv(env)
+ {}
+
+ MOZ_IMPLICIT Type(const char16_t* str, JNIEnv* env = GetJNIForThread())
+ : Ref<String>(GetString(env, nsDependentString(str)))
+ , mEnv(env)
+ {}
+
+ MOZ_IMPLICIT Type(const nsACString& str, JNIEnv* env = GetJNIForThread())
+ : Ref<String>(GetString(env, NS_ConvertUTF8toUTF16(str)))
+ , mEnv(env)
+ {}
+
+ MOZ_IMPLICIT Type(const char* str, JNIEnv* env = GetJNIForThread())
+ : Ref<String>(GetString(env, NS_ConvertUTF8toUTF16(str)))
+ , mEnv(env)
+ {}
+
+ ~Type()
+ {
+ if (mEnv) {
+ mEnv->DeleteLocalRef(Get());
+ }
+ }
+};
+
+
+// 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__
new file mode 100644
--- /dev/null
+++ b/widget/android/jni/Types.h
@@ -0,0 +1,120 @@
+#ifndef mozilla_jni_Types_h__
+#define mozilla_jni_Types_h__
+
+#include <jni.h>
+
+#include "mozilla/jni/Refs.h"
+#include "AndroidBridge.h"
+
+namespace mozilla {
+namespace jni {
+namespace {
+
+// TypeAdapter specializations are the interfaces between naive (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>> {
+ static constexpr auto Call = &JNIEnv::CallObjectMethodA;
+ static constexpr auto StaticCall = &JNIEnv::CallStaticObjectMethodA;
+ static constexpr auto Get = &JNIEnv::GetObjectField;
+ static constexpr auto StaticGet = &JNIEnv::GetStaticObjectField;
+
+ static LocalRef<Cls> ToNative(JNIEnv* env, jobject instance) {
+ return LocalRef<Cls>::Adopt(env, instance);
+ }
+};
+
+template<class Cls> constexpr jobject
+ (JNIEnv::*TypeAdapter<LocalRef<Cls>>::Call)(jobject, jmethodID, jvalue*);
+template<class Cls> constexpr jobject
+ (JNIEnv::*TypeAdapter<LocalRef<Cls>>::StaticCall)(jclass, jmethodID, jvalue*);
+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> struct TypeAdapter<Ref<Cls>> {
+ static constexpr auto Set = &JNIEnv::SetObjectField;
+ static constexpr auto StaticSet = &JNIEnv::SetStaticObjectField;
+
+ static jobject FromNative(JNIEnv*, const Ref<Cls>& instance) {
+ return instance.Get();
+ }
+};
+
+template<class Cls> constexpr void
+ (JNIEnv::*TypeAdapter<Ref<Cls>>::Set)(jobject, jfieldID, jobject);
+template<class Cls> constexpr void
+ (JNIEnv::*TypeAdapter<Ref<Cls>>::StaticSet)(jclass, jfieldID, jobject);
+
+
+// jstring has its own Param type.
+template<> struct TypeAdapter<class Param<String>::Type>
+ : public TypeAdapter<String::Ref>
+{};
+
+
+#define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName) \
+ \
+ template<> struct TypeAdapter<NativeType> { \
+ 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 JNIType FromNative(JNIEnv*, NativeType val) { \
+ return static_cast<JNIType>(val); \
+ } \
+ static NativeType ToNative(JNIEnv*, JNIType val) { \
+ return static_cast<NativeType>(val); \
+ } \
+ }; \
+ \
+ constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Call) \
+ (jobject, jmethodID, jvalue*); \
+ constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticCall) \
+ (jclass, jmethodID, jvalue*); \
+ constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::Get) \
+ (jobject, jfieldID); \
+ constexpr JNIType (JNIEnv::*TypeAdapter<NativeType>::StaticGet) \
+ (jclass, jfieldID); \
+ constexpr void (JNIEnv::*TypeAdapter<NativeType>::Set) \
+ (jobject, jfieldID, JNIType); \
+ constexpr void (JNIEnv::*TypeAdapter<NativeType>::StaticSet) \
+ (jclass, jfieldID, JNIType)
+
+
+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
+} // namespace jni
+} // namespace mozilla
+
+#endif // mozilla_jni_Types_h__
new file mode 100644
--- /dev/null
+++ b/widget/android/jni/Utils.cpp
@@ -0,0 +1,43 @@
+#include "Utils.h"
+
+#include "mozilla/Assertions.h"
+
+#include "GeneratedJNIWrappers.h"
+#include "Refs.h"
+
+namespace mozilla {
+namespace jni {
+
+bool ThrowException(JNIEnv *aEnv, const char *aClass,
+ const char *aMessage)
+{
+ MOZ_ASSERT(aEnv, "Invalid thread JNI env");
+
+ ClassObject::LocalRef cls =
+ ClassObject::LocalRef::Adopt(aEnv->FindClass(aClass));
+ MOZ_ASSERT(cls, "Cannot find exception class");
+
+ return !aEnv->ThrowNew(cls.Get(), aMessage);
+}
+
+void HandleUncaughtException(JNIEnv *aEnv)
+{
+ MOZ_ASSERT(aEnv, "Invalid thread JNI env");
+
+ if (!aEnv->ExceptionCheck()) {
+ return;
+ }
+
+ Throwable::LocalRef e =
+ Throwable::LocalRef::Adopt(aEnv->ExceptionOccurred());
+ MOZ_ASSERT(e);
+
+ aEnv->ExceptionClear();
+ widget::GeckoAppShell::HandleUncaughtException(nullptr, e);
+
+ // Should be dead by now...
+ MOZ_CRASH("Failed to handle uncaught exception");
+}
+
+} // jni
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/widget/android/jni/Utils.h
@@ -0,0 +1,27 @@
+#ifndef mozilla_jni_Utils_h__
+#define mozilla_jni_Utils_h__
+
+#include <jni.h>
+
+#include "mozilla/Types.h"
+
+/* See the comment in AndroidBridge about this function before using it */
+extern "C" MOZ_EXPORT JNIEnv * GetJNIForThread();
+
+namespace mozilla {
+namespace jni {
+
+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);
+}
+
+void HandleUncaughtException(JNIEnv *aEnv);
+
+} // jni
+} // mozilla
+
+#endif // mozilla_jni_Utils_h__
new file mode 100644
--- /dev/null
+++ b/widget/android/jni/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; c-basic-offset: 4; 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',
+ 'Refs.h',
+ 'Types.h',
+ 'Utils.h',
+]
+
+SOURCES += [
+ 'Utils.cpp',
+]
+
+FAIL_ON_WARNINGS = True
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '/widget/android',
+]
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -1,16 +1,17 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
DIRS += [
'bindings',
+ 'jni',
]
XPIDL_SOURCES += [
'nsIAndroidBridge.idl',
]
XPIDL_MODULE = 'widget_android'