Bug 984458 - b. Add threading support to NativeJSContainer; r=blassey

This commit is contained in:
Jim Chen 2014-04-01 15:16:53 -04:00
Родитель d291d3bec4
Коммит 1447ea535f
3 изменённых файлов: 117 добавлений и 5 удалений

Просмотреть файл

@ -10,6 +10,10 @@ import org.mozilla.gecko.mozglue.JNITarget;
/**
* NativeJSContainer is a wrapper around the SpiderMonkey JSAPI to make it possible to
* access Javascript objects in Java.
*
* A container must only be used on the thread it is attached to. To use it on another
* thread, call {@link #clone()} to make a copy, and use the copy on the other thread.
* When a copy is first used, it becomes attached to the thread using it.
*/
@JNITarget
public final class NativeJSContainer extends NativeJSObject
@ -20,6 +24,13 @@ public final class NativeJSContainer extends NativeJSObject
mNativeObject = nativeObject;
}
/**
* Make a copy of this container for use by another thread. When the copy is first used,
* it becomes attached to the thread using it.
*/
@Override
public native NativeJSContainer clone();
/**
* Dispose all associated native objects. Subsequent use of any objects derived from
* this container will throw a NullPointerException.

Просмотреть файл

@ -571,6 +571,25 @@ Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode(JNIEnv * ar
#ifdef JNI_STUBS
typedef jobject (*Java_org_mozilla_gecko_util_NativeJSContainer_clone_t)(JNIEnv *, jobject);
static Java_org_mozilla_gecko_util_NativeJSContainer_clone_t f_Java_org_mozilla_gecko_util_NativeJSContainer_clone;
extern "C" NS_EXPORT jobject JNICALL
Java_org_mozilla_gecko_util_NativeJSContainer_clone(JNIEnv * arg0, jobject arg1) {
if (!f_Java_org_mozilla_gecko_util_NativeJSContainer_clone) {
arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
"JNI Function called before it was loaded");
return nullptr;
}
return f_Java_org_mozilla_gecko_util_NativeJSContainer_clone(arg0, arg1);
}
#endif
#ifdef JNI_BINDINGS
xul_dlsym("Java_org_mozilla_gecko_util_NativeJSContainer_clone", &f_Java_org_mozilla_gecko_util_NativeJSContainer_clone);
#endif
#ifdef JNI_STUBS
typedef void (*Java_org_mozilla_gecko_util_NativeJSContainer_dispose_t)(JNIEnv *, jobject);
static Java_org_mozilla_gecko_util_NativeJSContainer_dispose_t f_Java_org_mozilla_gecko_util_NativeJSContainer_dispose;
extern "C" NS_EXPORT void JNICALL

Просмотреть файл

@ -5,6 +5,7 @@
#include "NativeJSContainer.h"
#include "AndroidBridge.h"
#include "prthread.h"
using namespace mozilla;
using namespace mozilla::widget;
@ -60,6 +61,58 @@ public:
}
}
static jobject CloneInstance(JNIEnv* env, jobject instance) {
NativeJSContainer* const container = FromInstance(env, instance);
if (!container || !container->EnsureObject(env)) {
return nullptr;
}
JSContext* const cx = container->mThreadContext;
JS::RootedObject object(cx, container->mJSObject);
MOZ_ASSERT(object);
JSAutoStructuredCloneBuffer buffer;
if (!buffer.write(cx, JS::RootedValue(cx, JS::ObjectValue(*object)))) {
AndroidBridge::ThrowException(env,
"java/lang/UnsupportedOperationException",
"Cannot serialize object");
return nullptr;
}
return CreateInstance(env, new NativeJSContainer(cx, Move(buffer)));
}
// Make sure we have a JSObject and deserialize if necessary/possible
bool EnsureObject(JNIEnv* env) {
if (mJSObject) {
if (PR_GetCurrentThread() != mThread) {
AndroidBridge::ThrowException(env,
"java/lang/IllegalThreadStateException",
"Using NativeJSObject off its thread");
return false;
}
return true;
}
if (!SwitchContextToCurrentThread()) {
AndroidBridge::ThrowException(env,
"java/lang/IllegalThreadStateException",
"Not available for this thread");
return false;
}
JS::RootedValue value(mThreadContext);
MOZ_ASSERT(mBuffer.data());
MOZ_ALWAYS_TRUE(mBuffer.read(mThreadContext, &value));
if (value.isObject()) {
mJSObject = &value.toObject();
}
if (!mJSObject) {
AndroidBridge::ThrowException(env,
"java/lang/IllegalStateException", "Cannot deserialize data");
return false;
}
mBuffer.clear();
return true;
}
private:
static jclass jNativeJSContainer;
static jfieldID jContainerNativeObject;
@ -77,16 +130,38 @@ private:
return newObject;
}
JSContext* const mContext;
// Root JS object
const JS::Heap<JSObject*> mJSObject;
// Thread that the object is valid on
PRThread* mThread;
// Context that the object is valid in
JSContext* mThreadContext;
// Deserialized object, or nullptr if object is in serialized form
JS::Heap<JSObject*> mJSObject;
// Serialized object, or empty if object is in deserialized form
JSAutoStructuredCloneBuffer mBuffer;
// Create a new container containing the given object
// Create a new container containing the given deserialized object
NativeJSContainer(JSContext* cx, JS::HandleObject object)
: mContext(cx)
: mThread(PR_GetCurrentThread())
, mThreadContext(cx)
, mJSObject(object)
{
}
// Create a new container containing the given serialized object
NativeJSContainer(JSContext* cx, JSAutoStructuredCloneBuffer&& buffer)
: mThread(PR_GetCurrentThread())
, mThreadContext(cx)
, mBuffer(Forward<JSAutoStructuredCloneBuffer>(buffer))
{
}
bool SwitchContextToCurrentThread() {
PRThread* const currentThread = PR_GetCurrentThread();
if (currentThread == mThread) {
return true;
}
return false;
}
};
jclass NativeJSContainer::jNativeJSContainer = 0;
@ -111,4 +186,11 @@ Java_org_mozilla_gecko_util_NativeJSContainer_dispose(JNIEnv* env, jobject insta
NativeJSContainer::DisposeInstance(env, instance);
}
NS_EXPORT jobject JNICALL
Java_org_mozilla_gecko_util_NativeJSContainer_clone(JNIEnv* env, jobject instance)
{
MOZ_ASSERT(env);
return NativeJSContainer::CloneInstance(env, instance);
}
} // extern "C"