Reviewed by Jon.
[WebKit-https.git] / WebCore / bridge / jni / jni_jsobject.cpp
index 602aca7ef8d2e25ab2f1fe29b2f5acb40042e024..3ced4a7c5c2f10f1c6f9d57ec002483157c6b01b 100644 (file)
 #include "config.h"
 #include "jni_jsobject.h"
 
+#include "Frame.h"
+#include "kjs_proxy.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreViewFactory.h"
 #include "jni_runtime.h"
 #include "jni_utility.h"
 #include "runtime_object.h"
 #include <kjs/ExecState.h>
 #include <kjs/JSGlobalObject.h>
 #include <kjs/interpreter.h>
-
 #include <wtf/Assertions.h>
 
+using WebCore::Frame;
+
 using namespace KJS::Bindings;
 using namespace KJS;
 
@@ -43,19 +48,135 @@ using namespace KJS;
 #define JS_LOG(formatAndArgs...) ((void)0)
 #else
 #define JS_LOG(formatAndArgs...) { \
-    fprintf (stderr, "%s(%p,%p):  ", __PRETTY_FUNCTION__, RootObject::runLoop(), CFRunLoopGetCurrent()); \
+    fprintf (stderr, "%s(%p,%p):  ", __PRETTY_FUNCTION__, _performJavaScriptRunLoop, CFRunLoopGetCurrent()); \
     fprintf(stderr, formatAndArgs); \
 }
 #endif
 
 #define UndefinedHandle 1
 
+static CFRunLoopSourceRef _performJavaScriptSource;
+static CFRunLoopRef _performJavaScriptRunLoop;
+
+// May only be set by dispatchToJavaScriptThread().
+static CFRunLoopSourceRef completionSource;
+
+static void completedJavaScriptAccess (void *i)
+{
+    assert (CFRunLoopGetCurrent() != _performJavaScriptRunLoop);
+    
+    JSObjectCallContext *callContext = (JSObjectCallContext *)i;
+    CFRunLoopRef runLoop = (CFRunLoopRef)callContext->originatingLoop;
+    
+    assert (CFRunLoopGetCurrent() == runLoop);
+    
+    CFRunLoopStop(runLoop);
+}
+
+static pthread_once_t javaScriptAccessLockOnce = PTHREAD_ONCE_INIT;
+static pthread_mutex_t javaScriptAccessLock;
+static int javaScriptAccessLockCount = 0;
+
+static void initializeJavaScriptAccessLock()
+{
+    pthread_mutexattr_t attr;
+    
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
+    
+    pthread_mutex_init(&javaScriptAccessLock, &attr);
+}
+
+static inline void lockJavaScriptAccess()
+{
+    // Perhaps add deadlock detection?
+    pthread_once(&javaScriptAccessLockOnce, initializeJavaScriptAccessLock);
+    pthread_mutex_lock(&javaScriptAccessLock);
+    javaScriptAccessLockCount++;
+}
+
+static inline void unlockJavaScriptAccess()
+{
+    javaScriptAccessLockCount--;
+    pthread_mutex_unlock(&javaScriptAccessLock);
+}
+
+static void dispatchToJavaScriptThread(JSObjectCallContext *context)
+{
+    // This lock guarantees that only one thread can invoke
+    // at a time, and also guarantees that completionSource;
+    // won't get clobbered.
+    lockJavaScriptAccess();
+    
+    CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
+    
+    assert (currentRunLoop != _performJavaScriptRunLoop);
+    
+    // Setup a source to signal once the invocation of the JavaScript
+    // call completes.
+    //
+    // FIXME:  This could be a potential performance issue.  Creating and
+    // adding run loop sources is expensive.  We could create one source 
+    // per thread, as needed, instead.
+    context->originatingLoop = currentRunLoop;
+    CFRunLoopSourceContext sourceContext = {0, context, NULL, NULL, NULL, NULL, NULL, NULL, NULL, completedJavaScriptAccess};
+    completionSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext);
+    CFRunLoopAddSource(currentRunLoop, completionSource, kCFRunLoopDefaultMode);
+    
+    // Wakeup JavaScript access thread and make it do it's work.
+    CFRunLoopSourceSignal(_performJavaScriptSource);
+    if (CFRunLoopIsWaiting(_performJavaScriptRunLoop))
+        CFRunLoopWakeUp(_performJavaScriptRunLoop);
+    
+    // Wait until the JavaScript access thread is done.
+    CFRunLoopRun ();
+    
+    CFRunLoopRemoveSource(currentRunLoop, completionSource, kCFRunLoopDefaultMode);
+    CFRelease (completionSource);
+    
+    unlockJavaScriptAccess();
+}
+
+static void performJavaScriptAccess(void*)
+{
+    assert (CFRunLoopGetCurrent() == _performJavaScriptRunLoop);
+    
+    // Dispatch JavaScript calls here.
+    CFRunLoopSourceContext sourceContext;
+    CFRunLoopSourceGetContext (completionSource, &sourceContext);
+    JSObjectCallContext *callContext = (JSObjectCallContext *)sourceContext.info;    
+    CFRunLoopRef originatingLoop = callContext->originatingLoop;
+    
+    JavaJSObject::invoke (callContext);
+    
+    // Signal the originating thread that we're done.
+    CFRunLoopSourceSignal (completionSource);
+    if (CFRunLoopIsWaiting(originatingLoop))
+        CFRunLoopWakeUp(originatingLoop);
+}
+
+// Must be called from the thread that will be used to access JavaScript.
+void JavaJSObject::initializeJNIThreading() {
+    // Should only be called once.
+    ASSERT(!_performJavaScriptRunLoop);
+    
+    // Assume that we can retain this run loop forever.  It'll most 
+    // likely (always?) be the main loop.
+    _performJavaScriptRunLoop = (CFRunLoopRef)CFRetain(CFRunLoopGetCurrent());
+    
+    // Setup a source the other threads can use to signal the _runLoop
+    // thread that a JavaScript call needs to be invoked.
+    CFRunLoopSourceContext sourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, performJavaScriptAccess};
+    _performJavaScriptSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext);
+    CFRunLoopAddSource(_performJavaScriptRunLoop, _performJavaScriptSource, kCFRunLoopDefaultMode);
+}
+
 static bool isJavaScriptThread()
 {
-    return (RootObject::runLoop() == CFRunLoopGetCurrent());
+    return (_performJavaScriptRunLoop == CFRunLoopGetCurrent());
 }
 
-jvalue JavaJSObject::invoke (JSObjectCallContext *context)
+jvalue JavaJSObject::invoke(JSObjectCallContext *context)
 {
     jvalue result;
 
@@ -64,7 +185,7 @@ jvalue JavaJSObject::invoke (JSObjectCallContext *context)
     if (!isJavaScriptThread()) {        
         // Send the call context to the thread that is allowed to
         // call JavaScript.
-        RootObject::dispatchToJavaScriptThread(context);
+        dispatchToJavaScriptThread(context);
         result = context->result;
     }
     else {
@@ -316,6 +437,17 @@ void JavaJSObject::finalize() const
         rootObject->gcUnprotect(_imp);
 }
 
+static PassRefPtr<RootObject> createRootObject(void* nativeHandle)
+{
+    NSView *view = (NSView *)nativeHandle;
+    WebCoreFrameBridge *bridge = [[WebCoreViewFactory sharedFactory] bridgeForView:view];
+    if (!bridge)
+        return 0;
+    
+    Frame* frame = [bridge _frame];
+    return frame->createRootObject(nativeHandle, frame->scriptProxy()->globalObject());
+}
+
 // We're either creating a 'Root' object (via a call to JavaJSObject.getWindow()), or
 // another JavaJSObject.
 jlong JavaJSObject::createNative(jlong nativeHandle)
@@ -328,10 +460,6 @@ jlong JavaJSObject::createNative(jlong nativeHandle)
     if (findProtectingRootObject(jlong_to_impptr(nativeHandle)))
         return nativeHandle;
 
-    CreateRootObjectFunction createRootObject = RootObject::createRootObject();
-    if (!createRootObject)
-        return ptr_to_jlong(0);
-
     RefPtr<RootObject> rootObject = createRootObject(jlong_to_ptr(nativeHandle));
 
     // If rootObject is !NULL We must have been called via netscape.javascript.JavaJSObject.getWindow(),
@@ -444,8 +572,8 @@ JSValue *JavaJSObject::convertJObjectToValue (jobject theObject) const
     // possible to pass primitive types from the Java to JavaScript.
     // See section 22.7 of 'JavaScript:  The Definitive Guide, 4th Edition',
     // figure 22-4.
-    jobject classOfInstance = callJNIObjectMethod(theObject, "getClass", "()Ljava/lang/Class;");
-    jstring className = (jstring)callJNIObjectMethod(classOfInstance, "getName", "()Ljava/lang/String;");
+    jobject classOfInstance = callJNIMethod<jobject>(theObject, "getClass", "()Ljava/lang/Class;");
+    jstring className = (jstring)callJNIMethod<jobject>(classOfInstance, "getName", "()Ljava/lang/String;");
     
     // Only the sun.plugin.javascript.webkit.JSObject has a member called nativeJSObject. This class is
     // created above to wrap internal browser objects. The constructor of this class takes the native