Objective-C API: Need a way to use the Objective-C JavaScript API with WebKit
authormhahnenberg@apple.com <mhahnenberg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Feb 2013 20:00:33 +0000 (20:00 +0000)
committermhahnenberg@apple.com <mhahnenberg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Feb 2013 20:00:33 +0000 (20:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=106059

Source/JavaScriptCore:

Reviewed by Geoffrey Garen.

* API/JSBase.h: Renamed enable flag for API.
* API/JSBlockAdaptor.h: Using new flag.
* API/JSBlockAdaptor.mm: Ditto.
* API/JSContext.h: Add convenience C API conversion function for JSGlobalContextRef.
* API/JSContext.mm:
(-[JSContext JSGlobalContextRef]): Implementation of C API convenience function.
(-[JSContext initWithVirtualMachine:]): We don't use the m_apiData field any more.
(-[JSContext initWithGlobalContextRef:]): init method for allocating new JSContexts given a JSGlobalContextRef.
(-[JSContext dealloc]): No more m_apiData.
(-[JSContext wrapperForObjCObject:]): Renamed wrapperForObject.
(-[JSContext wrapperForJSObject:]): Fetches or allocates the JSValue for the specified JSValueRef in this JSContext.
(+[JSContext contextWithGlobalContextRef:]): Helper function to grab the lightweight JSContext wrapper for a given
JSGlobalContextRef from the global wrapper cache or allocate a new one if there isn't already one.
* API/JSContextInternal.h: New flag, new method declaration for initWithGlobalContextRef.
* API/JSExport.h: New flag.
* API/JSValue.h: New flag and new C API convenience method.
* API/JSValue.mm:
(-[JSValue JSValueRef]): Implementation of the C API convenience method.
(objectToValueWithoutCopy):
(+[JSValue valueWithValue:inContext:]): We now ask the JSContext for an Objective-C JSValue wrapper, which it can cache
in its internal JSWrapperMap.
* API/JSValueInternal.h:
* API/JSVirtualMachine.h:
* API/JSVirtualMachine.mm: Added global cache that maps JSContextGroupRef -> JSVirtualMachine lightweight wrappers.
(wrapperCacheLock):
(initWrapperCache):
(+[JSVMWrapperCache addWrapper:forJSContextGroupRef:]):
(+[JSVMWrapperCache wrapperForJSContextGroupRef:]):
(-[JSVirtualMachine init]):
(-[JSVirtualMachine initWithContextGroupRef:]):
(-[JSVirtualMachine dealloc]):
(+[JSVirtualMachine virtualMachineWithContextGroupRef:]):
(-[JSVirtualMachine contextForGlobalContextRef:]):
(-[JSVirtualMachine addContext:forGlobalContextRef:]):
* API/JSVirtualMachineInternal.h:
* API/JSWrapperMap.h:
* API/JSWrapperMap.mm:
(-[JSObjCClassInfo allocateConstructorAndPrototypeWithSuperClassInfo:]): We use the JSObjectSetPrototype C API call because
setting the __proto__ property causes all sorts of bad things to happen behind the scenes, which can cause crashes based on
when it gets called.
(-[JSWrapperMap initWithContext:]):
(-[JSWrapperMap jsWrapperForObject:]):
(-[JSWrapperMap objcWrapperForJSValueRef:]):
* API/JavaScriptCore.h:
* API/ObjCCallbackFunction.h:
* API/ObjCCallbackFunction.mm:
(ObjCCallbackFunction::ObjCCallbackFunction): We never actually should have retained the target in the case that we had a
block as a callback. Blocks are initially allocated on the stack and are only moved to the heap if we call their copy method.
Retaining the block on the stack was a bad idea because if that stack frame ever went away and we called the block later,
we'd crash and burn.
(ObjCCallbackFunction::setContext): We need a new setter for when the weak reference to a JSContext inside an ObjCCallbackFunction
disappears, we can allocate a new one in its place.
(ObjCCallbackFunction):
(objCCallbackFunctionCallAsFunction): Reset the callback's context if it's ever destroyed.
(objCCallbackFunctionForInvocation): Again, don't set the __proto__ property because it uses black magic that can cause us to crash
depending on when this is called.
(objCCallbackFunctionForBlock): Here is where we copy the block to the heap when we're first creating the callback object for it.
* API/tests/testapi.c:
(main):
* API/tests/testapi.mm: We're going to get rid of the automatic block conversion, since that is causing leaks. I changed it
here in this test just so that it wouldn't mask any other potential leaks. Also modified some of the tests since JSContexts are
just lightweight wrappers now, we're not guaranteed to get the same pointer back from the call to [JSValue context] as the one
that the value was created in.
(-[TestObject callback:]):
* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/JSGlobalData.cpp:
(JSC::JSGlobalData::JSGlobalData): No more m_apiData.
* runtime/JSGlobalData.h: Ditto.
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::JSGlobalObject): Ditto.
* runtime/JSGlobalObject.h:

Source/WebCore:

Reviewed by Geoffrey Garen.

* WebCore.exp.in:
* bindings/js/JSDOMWindowShell.cpp:
(WebCore::JSDOMWindowShell::setWindow): Since we're basically abandoning a JSDOMWindow here, we call
garbageCollectSoon().
* bindings/js/JSDOMWindowShell.h:
* bindings/js/ScriptController.h: New function to get the JSContext for the global object of the current main world.
* bindings/js/ScriptControllerMac.mm:
(WebCore::ScriptController::javaScriptContext): Ditto.
* bindings/objc/WebScriptObject.h: Added ifdef guards. Also new convenience conversion function for the JSC Obj-C API.
* bindings/objc/WebScriptObject.mm: JSC::JSValue and JSValue conflict with one another, so we have to be more specific.
(-[WebScriptObject _globalContextRef]): Useful helper function for getting the JSGlobalContextRef of a particular WebScriptObject.
(-[WebScriptObject callWebScriptMethod:withArguments:]):
(-[WebScriptObject evaluateWebScript:]):
(-[WebScriptObject valueForKey:]):
(-[WebScriptObject webScriptValueAtIndex:]):
(+[WebScriptObject _convertValueToObjcValue:JSC::originRootObject:rootObject:]):
(-[WebScriptObject JSValue]): Implementation of convenience WebScriptObject conversion function to new Objective-C API.
* bindings/objc/WebScriptObjectPrivate.h:

Source/WebKit/mac:

Reviewed by Geoffrey Garen.

Addition of appropriate delegate callbacks and support to the WebKit API.

* WebCoreSupport/WebFrameLoaderClient.mm:
* WebView/WebDelegateImplementationCaching.h:
(WebFrameLoadDelegateImplementationCache):
* WebView/WebFrame.h:
* WebView/WebFrame.mm:
(-[WebFrame _stringByEvaluatingJavaScriptFromString:forceUserGesture:]):
(-[WebFrame _stringByEvaluatingJavaScriptFromString:withGlobalObject:inScriptWorld:]):
(-[WebFrame _javaScriptContextForScriptWorld:]):
(-[WebFrame javaScriptContext]):
* WebView/WebFrameLoadDelegate.h:
* WebView/WebFramePrivate.h:
* WebView/WebScriptDebugDelegate.mm:
(-[WebScriptCallFrame _convertValueToObjcValue:JSC::]):
(-[WebScriptCallFrame exception]):
(-[WebScriptCallFrame evaluateWebScript:]):
* WebView/WebScriptWorld.h:
* WebView/WebScriptWorld.mm:
(+[WebScriptWorld scriptWorldForJavaScriptContext:]):
* WebView/WebView.mm:
(-[WebView _cacheFrameLoadDelegateImplementations]):
(aeDescFromJSValue):
(-[WebView aeDescByEvaluatingJavaScriptFromString:]):
(-[WebView _computedStyleIncludingVisitedInfo:forElement:]):

Source/WTF:

Reviewed by Geoffrey Garen.

* wtf/FeatureDefines.h: Added enable flag for JSC Objective-C API so it can be used in
export files.

Tools:

Reviewed by Geoffrey Garen.

Added new tests for the WebKit API portion of the JSC Objective-C API.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/mac/JSContextBackForwardCache1.html: Added.
* TestWebKitAPI/Tests/mac/JSContextBackForwardCache2.html: Added.
* TestWebKitAPI/Tests/mac/WebViewDidCreateJavaScriptContext.mm: Added.
(-[MyConsole log:]):
(-[MyConsole printHelloWorld]):
(-[MyConsole add:to:]):
(-[DidCreateJavaScriptContextFrameLoadDelegate webView:didFinishLoadForFrame:]):
(-[DidCreateJavaScriptContextFrameLoadDelegate webView:didCreateJavaScriptContext:forFrame:]):
(TestWebKitAPI):
(TestWebKitAPI::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@143637 268f45cc-cd09-0410-ab3c-d52691b4dbfc

53 files changed:
Source/JavaScriptCore/API/JSBase.h
Source/JavaScriptCore/API/JSBlockAdaptor.h
Source/JavaScriptCore/API/JSBlockAdaptor.mm
Source/JavaScriptCore/API/JSContext.h
Source/JavaScriptCore/API/JSContext.mm
Source/JavaScriptCore/API/JSContextInternal.h
Source/JavaScriptCore/API/JSExport.h
Source/JavaScriptCore/API/JSValue.h
Source/JavaScriptCore/API/JSValue.mm
Source/JavaScriptCore/API/JSValueInternal.h
Source/JavaScriptCore/API/JSVirtualMachine.h
Source/JavaScriptCore/API/JSVirtualMachine.mm
Source/JavaScriptCore/API/JSVirtualMachineInternal.h
Source/JavaScriptCore/API/JSWrapperMap.h
Source/JavaScriptCore/API/JSWrapperMap.mm
Source/JavaScriptCore/API/JavaScriptCore.h
Source/JavaScriptCore/API/ObjCCallbackFunction.h
Source/JavaScriptCore/API/ObjCCallbackFunction.mm
Source/JavaScriptCore/API/tests/testapi.c
Source/JavaScriptCore/API/tests/testapi.mm
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/runtime/JSGlobalData.cpp
Source/JavaScriptCore/runtime/JSGlobalData.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/WTF/ChangeLog
Source/WTF/wtf/FeatureDefines.h
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/bindings/js/JSDOMWindowShell.cpp
Source/WebCore/bindings/js/JSDOMWindowShell.h
Source/WebCore/bindings/js/ScriptController.h
Source/WebCore/bindings/js/ScriptControllerMac.mm
Source/WebCore/bindings/objc/WebScriptObject.h
Source/WebCore/bindings/objc/WebScriptObject.mm
Source/WebCore/bindings/objc/WebScriptObjectPrivate.h
Source/WebKit/mac/ChangeLog
Source/WebKit/mac/WebCoreSupport/WebFrameLoaderClient.mm
Source/WebKit/mac/WebView/WebDelegateImplementationCaching.h
Source/WebKit/mac/WebView/WebFrame.h
Source/WebKit/mac/WebView/WebFrame.mm
Source/WebKit/mac/WebView/WebFrameLoadDelegate.h
Source/WebKit/mac/WebView/WebFramePrivate.h
Source/WebKit/mac/WebView/WebScriptDebugDelegate.mm
Source/WebKit/mac/WebView/WebScriptWorld.h
Source/WebKit/mac/WebView/WebScriptWorld.mm
Source/WebKit/mac/WebView/WebView.mm
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/mac/JSContextBackForwardCache1.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/mac/JSContextBackForwardCache2.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/mac/WebViewDidCreateJavaScriptContext.mm [new file with mode: 0644]

index 8fd91b1..50e8f1e 100644 (file)
@@ -140,7 +140,8 @@ JS_EXPORT void JSGarbageCollect(JSContextRef ctx);
 #endif
 
 /* Enable the Objective-C API for platforms with a modern runtime. */
-#undef JS_OBJC_API_ENABLED
-#define JS_OBJC_API_ENABLED (defined(__clang__) && defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 && !defined(__i386__))
+#if !defined(JSC_OBJC_API_ENABLED)
+#define JSC_OBJC_API_ENABLED (defined(__clang__) && defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 && !defined(__i386__))
+#endif
 
 #endif /* JSBase_h */
index fd275d1..8be57dc 100644 (file)
@@ -25,7 +25,7 @@
 
 #import <JavaScriptCore/JavaScriptCore.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 @interface JSBlockAdaptor : NSObject
 
index f42084c..3feebdc 100644 (file)
@@ -26,7 +26,7 @@
 #include "config.h"
 #import "JavaScriptCore.h"
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 #import "APICast.h"
 #import "APIShims.h"
index 0f2148c..e0913c1 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
+#ifndef JSContext_h
+#define JSContext_h
+
 #include <JavaScriptCore/JavaScript.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 @class JSVirtualMachine, JSValue;
 
@@ -49,6 +52,10 @@ NS_CLASS_AVAILABLE(10_9, NA)
 // Evaluate a string of JavaScript code.
 - (JSValue *)evaluateScript:(NSString *)script;
 
+// Return the C API version of this context. This function is for convenience
+// at the boundaries when converting code from the C API to the Objective-C API.
+- (JSGlobalContextRef)globalContextRef;
+
 // This method retrieves the global object of the JavaScript execution context.
 // Instances of JSContext originating from WebKit will return a reference to the
 // WindowProxy object.
@@ -114,3 +121,5 @@ NS_CLASS_AVAILABLE(10_9, NA)
 @end
 
 #endif
+
+#endif // JSContext_h
index 3022e6d..5bfa335 100644 (file)
@@ -37,7 +37,7 @@
 #import "StrongInlines.h"
 #import <wtf/HashSet.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 @implementation JSContext {
     JSVirtualMachine *m_virtualMachine;
 
 @synthesize exceptionHandler;
 
+- (JSGlobalContextRef)globalContextRef
+{
+    return m_context;
+}
+
 - (id)init
 {
     return [self initWithVirtualMachine:[[[JSVirtualMachine alloc] init] autorelease]];
@@ -67,7 +72,6 @@
         context.exception = exceptionValue;
     };
 
-    toJS(m_context)->lexicalGlobalObject()->m_apiData = self;
     return self;
 }
 
 
 @implementation JSContext(Internal)
 
+- (id)initWithGlobalContextRef:(JSGlobalContextRef)context
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    JSC::JSGlobalObject* globalObject = toJS(context)->lexicalGlobalObject();
+    m_virtualMachine = [[JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&globalObject->globalData())] retain];
+    ASSERT(m_virtualMachine);
+    m_context = JSGlobalContextRetain(context);
+    m_wrapperMap = [[JSWrapperMap alloc] initWithContext:self];
+
+    self.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
+        context.exception = exceptionValue;
+    };
+
+    return self;
+}
+
 JSGlobalContextRef contextInternalContext(JSContext *context)
 {
     return context->m_context;
@@ -174,7 +197,6 @@ JSGlobalContextRef contextInternalContext(JSContext *context)
 
 - (void)dealloc
 {
-    toJS(m_context)->lexicalGlobalObject()->m_apiData = 0;
     [m_wrapperMap release];
     JSGlobalContextRelease(m_context);
     [m_virtualMachine release];
@@ -220,11 +242,28 @@ JSGlobalContextRef contextInternalContext(JSContext *context)
     [self release];
 }
 
-- (JSValue *)wrapperForObject:(id)object
+- (JSValue *)wrapperForObjCObject:(id)object
 {
     // Lock access to m_wrapperMap
     JSC::JSLockHolder lock(toJS(m_context));
-    return [m_wrapperMap wrapperForObject:object];
+    return [m_wrapperMap jsWrapperForObject:object];
+}
+
+- (JSValue *)wrapperForJSObject:(JSValueRef)value
+{
+    JSC::JSLockHolder lock(toJS(m_context));
+    return [m_wrapperMap objcWrapperForJSValueRef:value];
+}
+
++ (JSContext *)contextWithGlobalContextRef:(JSGlobalContextRef)globalContext
+{
+    JSVirtualMachine *virtualMachine = [JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&toJS(globalContext)->globalData())];
+    JSContext *context = [virtualMachine contextForGlobalContextRef:globalContext];
+    if (!context) {
+        context = [[[JSContext alloc] initWithGlobalContextRef:globalContext] autorelease];
+        [virtualMachine addContext:context forGlobalContextRef:globalContext];
+    }
+    return context;
 }
 
 @end
index 22532c4..803a65b 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
+#ifndef JSContextInternal_h
+#define JSContextInternal_h
+
 #import <JavaScriptCore/JavaScriptCore.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
-#import "JSContext.h"
+#import <JavaScriptCore/JSContext.h>
 
 struct CallbackData {
     CallbackData *next;
@@ -56,7 +59,9 @@ private:
 
 @interface JSContext(Internal)
 
-JSGlobalContextRef contextInternalContext(JSContext *);
+- (id)initWithGlobalContextRef:(JSGlobalContextRef)context;
+
+JS_EXPORT_PRIVATE JSGlobalContextRef contextInternalContext(JSContext *);
 
 - (void)notifyException:(JSValueRef)exception;
 - (JSValue *)valueFromNotifyException:(JSValueRef)exception;
@@ -65,10 +70,15 @@ JSGlobalContextRef contextInternalContext(JSContext *);
 - (void)beginCallbackWithData:(CallbackData *)callbackData thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments;
 - (void)endCallbackWithData:(CallbackData *)callbackData;
 
-- (JSValue *)wrapperForObject:(id)object;
+- (JSValue *)wrapperForObjCObject:(id)object;
+- (JSValue *)wrapperForJSObject:(JSValueRef)value;
+
++ (JSContext *)contextWithGlobalContextRef:(JSGlobalContextRef)globalContext;
 
 @property (readonly, retain) JSWrapperMap *wrapperMap;
 
 @end
 
 #endif
+
+#endif // JSContextInternal_h
index 53d1f9d..c18c6d3 100644 (file)
@@ -25,7 +25,7 @@
 
 #import <JavaScriptCore/JavaScriptCore.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 // When a JavaScript value is created from an instance of an Objective-C class
 // for which no copying conversion is specified a JavaScript wrapper object will
index 038155a..f8ca59d 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#if JS_OBJC_API_ENABLED
+#ifndef JSValue_h
+#define JSValue_h
+
+#if JSC_OBJC_API_ENABLED
 
 @class JSContext;
 
@@ -102,6 +105,10 @@ NS_CLASS_AVAILABLE(10_9, NA)
 + (JSValue *)valueWithNullInContext:(JSContext *)context;
 + (JSValue *)valueWithUndefinedInContext:(JSContext *)context;
 
+// Return the C API version of this value. This function is for convenience
+// at the boundaries when converting code from the C API to the Objective-C API.
+- (JSValueRef)JSValueRef;
+
 // Convert this value to a corresponding Objective-C object, according to the
 // conversion specified above.
 - (id)toObject;
@@ -292,3 +299,4 @@ JS_EXPORT extern NSString * const JSPropertyDescriptorSetKey;
 
 #endif
 
+#endif // JSValue_h
index d0824c9..9155a94 100644 (file)
@@ -24,7 +24,6 @@
  */
 
 #include "config.h"
-//#import "JSValue.h"
 
 #import "APICast.h"
 #import "APIShims.h"
@@ -45,7 +44,7 @@
 #import <wtf/text/WTFString.h>
 #import <wtf/text/StringHash.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 NSString * const JSPropertyDescriptorWritableKey = @"writable";
 NSString * const JSPropertyDescriptorEnumerableKey = @"enumerable";
@@ -58,6 +57,11 @@ NSString * const JSPropertyDescriptorSetKey = @"set";
     JSValueRef m_value;
 }
 
+- (JSValueRef)JSValueRef
+{
+    return m_value;
+}
+
 + (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context
 {
     return [JSValue valueWithValue:objectToValue(context, value) inContext:context];
@@ -890,7 +894,7 @@ static ObjcContainerConvertor::Task objectToValueWithoutCopy(JSContext *context,
         }
     }
 
-    return (ObjcContainerConvertor::Task){ object, valueInternalValue([context wrapperForObject:object]), ContainerNone };
+    return (ObjcContainerConvertor::Task){ object, valueInternalValue([context wrapperForObjCObject:object]), ContainerNone };
 }
 
 JSValueRef objectToValue(JSContext *context, id object)
@@ -941,7 +945,7 @@ JSValueRef valueInternalValue(JSValue * value)
 
 + (JSValue *)valueWithValue:(JSValueRef)value inContext:(JSContext *)context
 {
-    return [[[JSValue alloc] initWithValue:value inContext:context] autorelease];
+    return [context wrapperForJSObject:value];
 }
 
 - (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context
index acab206..643db01 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
+#ifndef JSValueInternal_h
+#define JSValueInternal_h
+
 #import <JavaScriptCore/JavaScriptCore.h>
-#import "JSValue.h"
+#import <JavaScriptCore/JSValue.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 @interface JSValue(Internal)
 
@@ -52,3 +55,5 @@ NSInvocation *typeToValueInvocationFor(const char* encodedType);
 NSInvocation *valueToTypeInvocationFor(const char* encodedType);
 
 #endif
+
+#endif // JSValueInternal_h
index 443c374..d76d5fc 100644 (file)
@@ -25,7 +25,7 @@
 
 #import <JavaScriptCore/JavaScriptCore.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 // An instance of JSVirtualMachine represents a single JavaScript "object space"
 // or set of execution resources. Thread safety is supported by locking the
index 1e52819..37f8c5c 100644 (file)
 
 #import "JavaScriptCore.h"
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 #import "APICast.h"
 #import "JSVirtualMachineInternal.h"
 
+static NSMapTable *globalWrapperCache = 0;
+
+static Mutex& wrapperCacheLock()
+{
+    DEFINE_STATIC_LOCAL(Mutex, mutex, ());
+    return mutex;
+}
+
+static void initWrapperCache()
+{
+    ASSERT(!globalWrapperCache);
+    NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
+    NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+    globalWrapperCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
+}
+
+static NSMapTable *wrapperCache()
+{
+    if (!globalWrapperCache)
+        initWrapperCache();
+    return globalWrapperCache;
+}
+
+@interface JSVMWrapperCache : NSObject
++ (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group;
++ (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group;
+@end
+
+@implementation JSVMWrapperCache
+
++ (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group
+{
+    MutexLocker locker(wrapperCacheLock());
+    NSMapInsert(wrapperCache(), group, wrapper);
+}
+
++ (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group
+{
+    MutexLocker locker(wrapperCacheLock());
+    return static_cast<JSVirtualMachine *>(NSMapGet(wrapperCache(), group));
+}
+
+@end
+
 @implementation JSVirtualMachine {
     JSContextGroupRef m_group;
+    NSMapTable *m_contextCache;
 }
 
 - (id)init
 {
+    JSContextGroupRef group = JSContextGroupCreate();
+    self = [self initWithContextGroupRef:group];
+    // The extra JSContextGroupRetain is balanced here.
+    JSContextGroupRelease(group);
+    return self;
+}
+
+- (id)initWithContextGroupRef:(JSContextGroupRef)group
+{
     self = [super init];
     if (!self)
         return nil;
-
-    m_group = JSContextGroupCreate();
-    toJS(m_group)->m_apiData = self;
+    
+    m_group = JSContextGroupRetain(group);
+    
+    NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
+    NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+    m_contextCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
+    
     return self;
 }
 
 
 - (void)dealloc
 {
-    toJS(m_group)->m_apiData = 0;
     JSContextGroupRelease(m_group);
     [super dealloc];
 }
@@ -63,7 +120,28 @@ JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine)
     return virtualMachine->m_group;
 }
 
++ (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group
+{
+    JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:group];
+    if (!virtualMachine) {
+        virtualMachine = [[[JSVirtualMachine alloc] initWithContextGroupRef:group] autorelease];
+        [JSVMWrapperCache addWrapper:virtualMachine forJSContextGroupRef:group];
+    }
+    return virtualMachine;
+}
+
+- (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext
+{
+    return static_cast<JSContext *>(NSMapGet(m_contextCache, globalContext));
+}
+
+- (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext
+{
+    NSMapInsert(m_contextCache, globalContext, wrapper);
+}
+
 @end
 
+
 #endif
 
index dd86334..8fb8212 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#import "JSVirtualMachine.h"
+#ifndef JSVirtualMachineInternal_h
+#define JSVirtualMachineInternal_h
+
+#import <JavaScriptCore/JSVirtualMachine.h>
 #import <JavaScriptCore/JavaScriptCore.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 @interface JSVirtualMachine(Internal)
 
 JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *);
 
++ (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group;
+
+- (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext;
+- (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext;
+
 @end
 
 #endif
+
+#endif // JSVirtualMachineInternal_h
index af2fdc5..ce74a9c 100644 (file)
 #import <JSValueInternal.h>
 #import <objc/objc-runtime.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 @interface JSWrapperMap : NSObject
 
 - (id)initWithContext:(JSContext *)context;
 
-- (JSValue *)wrapperForObject:(id)object;
+- (JSValue *)jsWrapperForObject:(id)object;
+
+- (JSValue *)objcWrapperForJSValueRef:(JSValueRef)value;
 
 @end
 
index 1f30693..c088279 100644 (file)
@@ -26,7 +26,7 @@
 #include "config.h"
 #import "JavaScriptCore.h"
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 #import "APICast.h"
 #import "JSContextInternal.h"
@@ -382,7 +382,7 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco
         });
 
         // Set [Prototype].
-        prototype[@"__proto__"] = [JSValue valueWithValue:toRef(superClassInfo->m_prototype.get()) inContext:m_context];
+        JSObjectSetPrototype(contextInternalContext(m_context), toRef(m_prototype.get()), toRef(superClassInfo->m_prototype.get()));
 
         [constructor release];
         [prototype release];
@@ -425,7 +425,8 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco
 @implementation JSWrapperMap {
     JSContext *m_context;
     NSMutableDictionary *m_classMap;
-    JSC::WeakGCMap<id, JSC::JSObject> m_cachedWrappers;
+    JSC::WeakGCMap<id, JSC::JSObject> m_cachedJSWrappers;
+    NSMapTable *m_cachedObjCWrappers;
 }
 
 - (id)initWithContext:(JSContext *)context
@@ -434,6 +435,10 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco
     if (!self)
         return nil;
 
+    NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
+    NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+    m_cachedObjCWrappers = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
+    
     m_context = context;
     m_classMap = [[NSMutableDictionary alloc] init];
     return self;
@@ -461,9 +466,9 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco
     return m_classMap[cls] = [[[JSObjCClassInfo alloc] initWithContext:m_context forClass:cls superClassInfo:[self classInfoForClass:class_getSuperclass(cls)]] autorelease];
 }
 
-- (JSValue *)wrapperForObject:(id)object
+- (JSValue *)jsWrapperForObject:(id)object
 {
-    JSC::JSObject* jsWrapper = m_cachedWrappers.get(object);
+    JSC::JSObject* jsWrapper = m_cachedJSWrappers.get(object);
     if (jsWrapper)
         return [JSValue valueWithValue:toRef(jsWrapper) inContext:m_context];
 
@@ -482,7 +487,17 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco
     //     but still, would probably nicer if we made it so that only one associated object was required, broadcasting object dealloc.
     JSC::ExecState* exec = toJS(contextInternalContext(m_context));
     jsWrapper = toJS(exec, valueInternalValue(wrapper)).toObject(exec);
-    m_cachedWrappers.set(object, jsWrapper);
+    m_cachedJSWrappers.set(object, jsWrapper);
+    return wrapper;
+}
+
+- (JSValue *)objcWrapperForJSValueRef:(JSValueRef)value
+{
+    JSValue *wrapper = static_cast<JSValue *>(NSMapGet(m_cachedObjCWrappers, value));
+    if (!wrapper) {
+        wrapper = [[[JSValue alloc] initWithValue:value inContext:m_context] autorelease];
+        NSMapInsert(m_cachedObjCWrappers, value, wrapper);
+    }
     return wrapper;
 }
 
index 26bb3f1..d19cae2 100644 (file)
@@ -29,7 +29,7 @@
 #include <JavaScriptCore/JavaScript.h>
 #include <JavaScriptCore/JSStringRefCF.h>
 
-#if defined(__OBJC__) && JS_OBJC_API_ENABLED
+#if defined(__OBJC__) && JSC_OBJC_API_ENABLED
 
 #import "JSContext.h"
 #import "JSValue.h"
index 22acbb7..4d67e83 100644 (file)
@@ -25,7 +25,7 @@
 
 #import <JavaScriptCore/JavaScriptCore.h>
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 JSObjectRef objCCallbackFunctionForMethod(JSContext *, Class, Protocol *, BOOL isInstanceMethod, SEL, const char* types);
 JSObjectRef objCCallbackFunctionForBlock(JSContext *, id);
index 608663f..2f467e6 100644 (file)
 #include "config.h"
 #import "JavaScriptCore.h"
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 #import "APICast.h"
 #import "APIShims.h"
 #import "Error.h"
 #import "JSBlockAdaptor.h"
+#import "JSCJSValueInlines.h"
+#import "JSCell.h"
+#import "JSCellInlines.h"
 #import "JSContextInternal.h"
 #import "JSWrapperMap.h"
 #import "JSValueInternal.h"
@@ -426,8 +429,6 @@ public:
         , m_result(result)
     {
         ASSERT(type != CallbackInstanceMethod || instanceClass);
-        if (m_type != CallbackInstanceMethod)
-            [[m_invocation.get() target] retain];
     }
 
     ~ObjCCallbackFunction()
@@ -444,6 +445,12 @@ public:
         return m_context.get();
     }
 
+    void setContext(JSContext *context)
+    {
+        ASSERT(!m_context.get());
+        m_context.set(context);
+    }
+
     id wrappedBlock()
     {
         return m_type == CallbackBlock ? [m_invocation target] : nil;
@@ -474,11 +481,8 @@ static JSValueRef objCCallbackFunctionCallAsFunction(JSContextRef callerContext,
     ObjCCallbackFunction* callback = static_cast<ObjCCallbackFunction*>(JSObjectGetPrivate(function));
     JSContext *context = callback->context();
     if (!context) {
-        // FIXME: https://bugs.webkit.org/show_bug.cgi?id=105894
-        // Rather than requiring that the context be retained, it would probably be more
-        // appropriate to use a new JSContext instance (creating one if necessary).
-        *exception = toRef(JSC::createTypeError(toJS(callerContext), "Objective-C callback function context released"));
-        return JSValueMakeUndefined(callerContext);
+        context = [JSContext contextWithGlobalContextRef:toGlobalRef(toJS(callerContext)->lexicalGlobalObject()->globalExec())];
+        callback->setContext(context);
     }
 
     CallbackData callbackData;
@@ -610,7 +614,7 @@ static JSObjectRef objCCallbackFunctionForInvocation(JSContext *context, NSInvoc
     JSObjectRef functionObject = JSObjectMake(contextInternalContext(context), objCCallbackFunctionClass(), new ObjCCallbackFunction(context, invocation, type, instanceClass, arguments.release(), result.release()));
     JSValue *value = [JSValue valueWithValue:functionObject inContext:context];
     value[@"length"] = @(argumentCount);
-    value[@"__proto__"] = context[@"Function"][@"prototype"];
+    JSObjectSetPrototype(contextInternalContext(context), functionObject, valueInternalValue(context[@"Function"][@"prototype"]));
     value[@"toString"] = [context evaluateScript:@"(function(){ return '"
         "function <Objective-C>() {" "\\n"
         "    [native code]"          "\\n"
@@ -634,7 +638,7 @@ JSObjectRef objCCallbackFunctionForBlock(JSContext *context, id target)
         return 0;
     const char* signature = _Block_signature(target);
     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:signature]];
-    [invocation setTarget:target];
+    [invocation setTarget:[target copy]];
     return objCCallbackFunctionForInvocation(context, invocation, CallbackBlock, nil, signature);
 }
 
index 943ab05..cb150a1 100644 (file)
@@ -51,7 +51,7 @@ using std::isnan;
 
 #endif
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 void testObjectiveCAPI(void);
 #endif
 
@@ -1047,7 +1047,7 @@ int main(int argc, char* argv[])
     ::SetErrorMode(0);
 #endif
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
     testObjectiveCAPI();
 #endif
 
index d3ce281..fa5ebf5 100644 (file)
@@ -31,7 +31,7 @@ extern "C" const char * _Block_signature(id);
 extern int failed;
 extern "C" void testObjectiveCAPI(void);
 
-#if JS_OBJC_API_ENABLED
+#if JSC_OBJC_API_ENABLED
 
 @protocol ParentObject <JSExport>
 @end
@@ -57,7 +57,7 @@ extern "C" void testObjectiveCAPI(void);
 JSExportAs(testArgumentTypes,
 - (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
 );
-- (void)callback:(void(^)(int))block;
+- (void)callback:(JSValue *)function;
 @end
 
 @interface TestObject : ParentObject <TestObject>
@@ -85,9 +85,9 @@ JSExportAs(testArgumentTypes,
 {
     return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]];
 }
-- (void)callback:(void(^)(int))block
+- (void)callback:(JSValue *)function
 {
-    block(42);
+    [function callWithArguments:[NSArray arrayWithObject:[NSNumber numberWithInt:42]]];
 }
 @end
 
@@ -482,10 +482,8 @@ void testObjectiveCAPI()
         JSContext *context1 = [[JSContext alloc] init];
         JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine];
         JSValue *value = [JSValue valueWithDouble:42 inContext:context2];
-        checkResult(@"value.context == context2", value.context == context2);
         context1[@"passValueBetweenContexts"] = value;
         JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"];
-        checkResult(@"result.context == context1", result.context == context1);
         checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]);
     }
 }
index cfd24d2..fbb98b5 100644 (file)
@@ -1,3 +1,82 @@
+2013-02-20  Mark Hahnenberg  <mhahnenberg@apple.com>
+
+        Objective-C API: Need a way to use the Objective-C JavaScript API with WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=106059
+
+        Reviewed by Geoffrey Garen.
+        
+        * API/JSBase.h: Renamed enable flag for API.
+        * API/JSBlockAdaptor.h: Using new flag.
+        * API/JSBlockAdaptor.mm: Ditto.
+        * API/JSContext.h: Add convenience C API conversion function for JSGlobalContextRef.
+        * API/JSContext.mm: 
+        (-[JSContext JSGlobalContextRef]): Implementation of C API convenience function.
+        (-[JSContext initWithVirtualMachine:]): We don't use the m_apiData field any more.
+        (-[JSContext initWithGlobalContextRef:]): init method for allocating new JSContexts given a JSGlobalContextRef.
+        (-[JSContext dealloc]): No more m_apiData.
+        (-[JSContext wrapperForObjCObject:]): Renamed wrapperForObject. 
+        (-[JSContext wrapperForJSObject:]): Fetches or allocates the JSValue for the specified JSValueRef in this JSContext.
+        (+[JSContext contextWithGlobalContextRef:]): Helper function to grab the lightweight JSContext wrapper for a given
+        JSGlobalContextRef from the global wrapper cache or allocate a new one if there isn't already one.
+        * API/JSContextInternal.h: New flag, new method declaration for initWithGlobalContextRef.
+        * API/JSExport.h: New flag.
+        * API/JSValue.h: New flag and new C API convenience method.
+        * API/JSValue.mm:
+        (-[JSValue JSValueRef]): Implementation of the C API convenience method.
+        (objectToValueWithoutCopy):
+        (+[JSValue valueWithValue:inContext:]): We now ask the JSContext for an Objective-C JSValue wrapper, which it can cache
+        in its internal JSWrapperMap.
+        * API/JSValueInternal.h:
+        * API/JSVirtualMachine.h:
+        * API/JSVirtualMachine.mm: Added global cache that maps JSContextGroupRef -> JSVirtualMachine lightweight wrappers.
+        (wrapperCacheLock):
+        (initWrapperCache):
+        (+[JSVMWrapperCache addWrapper:forJSContextGroupRef:]):
+        (+[JSVMWrapperCache wrapperForJSContextGroupRef:]):
+        (-[JSVirtualMachine init]):
+        (-[JSVirtualMachine initWithContextGroupRef:]):
+        (-[JSVirtualMachine dealloc]):
+        (+[JSVirtualMachine virtualMachineWithContextGroupRef:]):
+        (-[JSVirtualMachine contextForGlobalContextRef:]):
+        (-[JSVirtualMachine addContext:forGlobalContextRef:]):
+        * API/JSVirtualMachineInternal.h:
+        * API/JSWrapperMap.h:
+        * API/JSWrapperMap.mm:
+        (-[JSObjCClassInfo allocateConstructorAndPrototypeWithSuperClassInfo:]): We use the JSObjectSetPrototype C API call because 
+        setting the __proto__ property causes all sorts of bad things to happen behind the scenes, which can cause crashes based on 
+        when it gets called.
+        (-[JSWrapperMap initWithContext:]):
+        (-[JSWrapperMap jsWrapperForObject:]):
+        (-[JSWrapperMap objcWrapperForJSValueRef:]):
+        * API/JavaScriptCore.h:
+        * API/ObjCCallbackFunction.h:
+        * API/ObjCCallbackFunction.mm:
+        (ObjCCallbackFunction::ObjCCallbackFunction): We never actually should have retained the target in the case that we had a 
+        block as a callback. Blocks are initially allocated on the stack and are only moved to the heap if we call their copy method.
+        Retaining the block on the stack was a bad idea because if that stack frame ever went away and we called the block later, 
+        we'd crash and burn.
+        (ObjCCallbackFunction::setContext): We need a new setter for when the weak reference to a JSContext inside an ObjCCallbackFunction
+        disappears, we can allocate a new one in its place.
+        (ObjCCallbackFunction):
+        (objCCallbackFunctionCallAsFunction): Reset the callback's context if it's ever destroyed.
+        (objCCallbackFunctionForInvocation): Again, don't set the __proto__ property because it uses black magic that can cause us to crash
+        depending on when this is called.
+        (objCCallbackFunctionForBlock): Here is where we copy the block to the heap when we're first creating the callback object for it.
+        * API/tests/testapi.c:
+        (main):
+        * API/tests/testapi.mm: We're going to get rid of the automatic block conversion, since that is causing leaks. I changed it 
+        here in this test just so that it wouldn't mask any other potential leaks. Also modified some of the tests since JSContexts are 
+        just lightweight wrappers now, we're not guaranteed to get the same pointer back from the call to [JSValue context] as the one 
+        that the value was created in.
+        (-[TestObject callback:]):
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * runtime/JSGlobalData.cpp:
+        (JSC::JSGlobalData::JSGlobalData): No more m_apiData.
+        * runtime/JSGlobalData.h: Ditto.
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::JSGlobalObject): Ditto.
+        * runtime/JSGlobalObject.h:
+
 2013-02-19  Filip Pizlo  <fpizlo@apple.com>
 
         DFG::SpeculativeJIT::compileInt32ToDouble() has an unnecessary case for constant operands
index 914a0cb..fadb1e2 100644 (file)
                86E3C614167BABD7006D760A /* JSExport.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60A167BAB87006D760A /* JSExport.h */; settings = {ATTRIBUTES = (Public, ); }; };
                86E3C615167BABD7006D760A /* JSVirtualMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60F167BAB87006D760A /* JSVirtualMachine.h */; settings = {ATTRIBUTES = (Public, ); }; };
                86E3C616167BABEE006D760A /* JSContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86E3C608167BAB87006D760A /* JSContext.mm */; };
-               86E3C617167BABEE006D760A /* JSContextInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C609167BAB87006D760A /* JSContextInternal.h */; };
+               86E3C617167BABEE006D760A /* JSContextInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C609167BAB87006D760A /* JSContextInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86E3C618167BABEE006D760A /* JSWrapperMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86E3C60B167BAB87006D760A /* JSWrapperMap.mm */; };
                86E3C619167BABEE006D760A /* JSWrapperMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60C167BAB87006D760A /* JSWrapperMap.h */; };
                86E3C61A167BABEE006D760A /* JSValue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86E3C60D167BAB87006D760A /* JSValue.mm */; };
-               86E3C61B167BABEE006D760A /* JSValueInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60E167BAB87006D760A /* JSValueInternal.h */; };
+               86E3C61B167BABEE006D760A /* JSValueInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60E167BAB87006D760A /* JSValueInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86E3C61C167BABEE006D760A /* JSVirtualMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86E3C610167BAB87006D760A /* JSVirtualMachine.mm */; };
-               86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C611167BAB87006D760A /* JSVirtualMachineInternal.h */; };
+               86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C611167BAB87006D760A /* JSVirtualMachineInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86E85539111B9968001AF51E /* JSStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E85538111B9968001AF51E /* JSStringBuilder.h */; };
                86EBF2FF1560F06A008E9222 /* NameConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86EBF2F91560F036008E9222 /* NameConstructor.cpp */; };
                86EBF3001560F06A008E9222 /* NameConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EBF2FA1560F036008E9222 /* NameConstructor.h */; };
                                0FB7F39515ED8E4600F167B2 /* ArrayConventions.h in Headers */,
                                0F63945515D07057006A597C /* ArrayProfile.h in Headers */,
                                BC18C3E70E16F5CD00B34460 /* ArrayPrototype.h in Headers */,
+                               86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */,
                                BC18C5240E16FC8A00B34460 /* ArrayPrototype.lut.h in Headers */,
+                               86E3C617167BABEE006D760A /* JSContextInternal.h in Headers */,
+                               86E3C61B167BABEE006D760A /* JSValueInternal.h in Headers */,
                                0FB7F39615ED8E4600F167B2 /* ArrayStorage.h in Headers */,
                                9688CB150ED12B4E001D649F /* AssemblerBuffer.h in Headers */,
                                86D3B2C510156BDE002865E7 /* AssemblerBufferWithConstantPool.h in Headers */,
                                865A30F1135007E100CDB49E /* JSCJSValueInlines.h in Headers */,
                                BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */,
                                86E3C613167BABD7006D760A /* JSContext.h in Headers */,
-                               86E3C617167BABEE006D760A /* JSContextInternal.h in Headers */,
                                BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */,
                                148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */,
                                978801411471AD920041B016 /* JSDateMath.h in Headers */,
                                BC18C42A0E16F5CD00B34460 /* JSType.h in Headers */,
                                6507D29E0E871E5E00D7D896 /* JSTypeInfo.h in Headers */,
                                86E3C612167BABD7006D760A /* JSValue.h in Headers */,
-                               86E3C61B167BABEE006D760A /* JSValueInternal.h in Headers */,
                                BC18C42C0E16F5CD00B34460 /* JSValueRef.h in Headers */,
                                BC18C42D0E16F5CD00B34460 /* JSVariableObject.h in Headers */,
                                86E3C615167BABD7006D760A /* JSVirtualMachine.h in Headers */,
-                               86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */,
                                A7482E93116A7CAD003B0712 /* JSWeakObjectMapRefInternal.h in Headers */,
                                A7482B9311671147003B0712 /* JSWeakObjectMapRefPrivate.h in Headers */,
                                1442566215EDE98D0066A49B /* JSWithScope.h in Headers */,
index ec6438a..c06cf5c 100644 (file)
@@ -187,7 +187,6 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, HeapType heapType)
     , m_timeoutCount(512)
 #endif
     , m_newStringsSinceLastHashConst(0)
-    , m_apiData(0)
 #if ENABLE(ASSEMBLER)
     , m_canUseAssembler(enableAssembler(executableAllocator))
 #endif
index 730be6e..dc9881f 100644 (file)
@@ -479,8 +479,6 @@ namespace JSC {
 
         JS_EXPORT_PRIVATE void discardAllCode();
 
-        void *m_apiData;
-
     private:
         friend class LLIntOffsetsExtractor;
         
index d00858c..f81a9f5 100644 (file)
@@ -116,7 +116,6 @@ JSGlobalObject::JSGlobalObject(JSGlobalData& globalData, Structure* structure, c
     , m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
     , m_evalEnabled(true)
     , m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable)
-    , m_apiData(0)
 {
 }
 
index a2edb9b..204b6cd 100644 (file)
@@ -196,8 +196,6 @@ public:
     bool hasDebugger() const { return m_debugger; }
     bool hasProfiler() const { return globalObjectMethodTable()->supportsProfiling(this); }
 
-    void* m_apiData;
-
 protected:
     JS_EXPORT_PRIVATE explicit JSGlobalObject(JSGlobalData&, Structure*, const GlobalObjectMethodTable* = 0);
 
index 107709b..4241517 100644 (file)
@@ -1,3 +1,13 @@
+2013-02-19  Mark Hahnenberg  <mhahnenberg@apple.com>
+
+        Objective-C API: Need a way to use the Objective-C JavaScript API with WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=106059
+
+        Reviewed by Geoffrey Garen.
+
+        * wtf/FeatureDefines.h: Added enable flag for JSC Objective-C API so it can be used in
+        export files.
+
 2013-02-21  Martin Robinson  <mrobinson@igalia.com>
 
         A couple more 'make dist' fixes for WebKitGTK+.
index 53f6166..b1e3ea2 100644 (file)
 #define ENABLE_JAVASCRIPT_I18N_API 0
 #endif
 
+#if !defined(JSC_OBJC_API_ENABLED)
+#define JSC_OBJC_API_ENABLED (defined(__clang__) && defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 && !defined(__i386__))
+#endif
+
 #if !defined(ENABLE_LEGACY_CSS_VENDOR_PREFIXES)
 #define ENABLE_LEGACY_CSS_VENDOR_PREFIXES 0
 #endif
index 455dee4..0cf8625 100644 (file)
@@ -1,3 +1,29 @@
+2013-02-19  Mark Hahnenberg  <mhahnenberg@apple.com>
+
+        Objective-C API: Need a way to use the Objective-C JavaScript API with WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=106059
+
+        Reviewed by Geoffrey Garen.
+
+        * WebCore.exp.in:
+        * bindings/js/JSDOMWindowShell.cpp:
+        (WebCore::JSDOMWindowShell::setWindow): Since we're basically abandoning a JSDOMWindow here, we call
+        garbageCollectSoon().
+        * bindings/js/JSDOMWindowShell.h:
+        * bindings/js/ScriptController.h: New function to get the JSContext for the global object of the current main world.
+        * bindings/js/ScriptControllerMac.mm: 
+        (WebCore::ScriptController::javaScriptContext): Ditto.
+        * bindings/objc/WebScriptObject.h: Added ifdef guards. Also new convenience conversion function for the JSC Obj-C API.
+        * bindings/objc/WebScriptObject.mm: JSC::JSValue and JSValue conflict with one another, so we have to be more specific.
+        (-[WebScriptObject _globalContextRef]): Useful helper function for getting the JSGlobalContextRef of a particular WebScriptObject.
+        (-[WebScriptObject callWebScriptMethod:withArguments:]):
+        (-[WebScriptObject evaluateWebScript:]):
+        (-[WebScriptObject valueForKey:]):
+        (-[WebScriptObject webScriptValueAtIndex:]):
+        (+[WebScriptObject _convertValueToObjcValue:JSC::originRootObject:rootObject:]):
+        (-[WebScriptObject JSValue]): Implementation of convenience WebScriptObject conversion function to new Objective-C API.
+        * bindings/objc/WebScriptObjectPrivate.h:
+
 2013-02-21  Kentaro Hara  <haraken@chromium.org>
 
         [V8] Rename add{Node,Object}ToGroup() to add{Node,Object}WrapperToGroup()
index 561a015..ff86a9d 100644 (file)
@@ -501,6 +501,9 @@ __ZN7WebCore16ScriptController10initScriptEPNS_15DOMWrapperWorldE
 __ZN7WebCore16ScriptController11createWorldEv
 __ZN7WebCore16ScriptController13executeScriptERKN3WTF6StringEb
 __ZN7WebCore16ScriptController17canExecuteScriptsENS_33ReasonForCallingCanExecuteScriptsE
+#if JSC_OBJC_API_ENABLED
+__ZN7WebCore16ScriptController17javaScriptContextEv
+#endif
 __ZN7WebCore16ScriptController18windowScriptObjectEv
 __ZN7WebCore16ScriptController20executeScriptInWorldEPNS_15DOMWrapperWorldERKN3WTF6StringEb
 __ZN7WebCore16ScriptController21processingUserGestureEv
index 32e5fa4..74dee06 100644 (file)
@@ -30,6 +30,7 @@
 #include "JSDOMWindowShell.h"
 
 #include "Frame.h"
+#include "GCController.h"
 #include "JSDOMWindow.h"
 #include "DOMWindow.h"
 #include "ScriptController.h"
@@ -60,6 +61,14 @@ void JSDOMWindowShell::destroy(JSCell* cell)
     static_cast<JSDOMWindowShell*>(cell)->JSDOMWindowShell::~JSDOMWindowShell();
 }
 
+void JSDOMWindowShell::setWindow(JSC::JSGlobalData& globalData, JSDOMWindow* window)
+{
+    ASSERT_ARG(window, window);
+    setTarget(globalData, window);
+    structure()->setGlobalObject(*JSDOMWindow::commonJSGlobalData(), window);
+    gcController().garbageCollectSoon();
+}
+
 void JSDOMWindowShell::setWindow(PassRefPtr<DOMWindow> domWindow)
 {
     // Replacing JSDOMWindow via telling JSDOMWindowShell to use the same DOMWindow it already uses makes no sense,
index 043c8ed..0e99cd1 100644 (file)
@@ -44,12 +44,7 @@ namespace WebCore {
         static void destroy(JSCell*);
 
         JSDOMWindow* window() const { return JSC::jsCast<JSDOMWindow*>(target()); }
-        void setWindow(JSC::JSGlobalData& globalData, JSDOMWindow* window)
-        {
-            ASSERT_ARG(window, window);
-            setTarget(globalData, window);
-            structure()->setGlobalObject(*JSDOMWindow::commonJSGlobalData(), window);
-        }
+        void setWindow(JSC::JSGlobalData&, JSDOMWindow*);
         void setWindow(PassRefPtr<DOMWindow>);
 
         static const JSC::ClassInfo s_info;
index 297cf7b..cd867e0 100644 (file)
@@ -26,6 +26,7 @@
 #include "JSDOMWindowShell.h"
 #include "ScriptControllerBase.h"
 #include "ScriptInstance.h"
+#include <JavaScriptCore/JSBase.h>
 #include <heap/Strong.h>
 #include <wtf/Forward.h>
 #include <wtf/RefPtr.h>
@@ -34,6 +35,7 @@
 #if PLATFORM(MAC)
 #include <wtf/RetainPtr.h>
 OBJC_CLASS WebScriptObject;
+OBJC_CLASS JSContext;
 #endif
 
 struct NPObject;
@@ -151,6 +153,9 @@ public:
 
 #if PLATFORM(MAC)
     WebScriptObject* windowScriptObject();
+#if JSC_OBJC_API_ENABLED
+    JSContext *javaScriptContext();
+#endif
 #endif
 
     JSC::JSObject* jsObjectForPluginElement(HTMLPlugInElement*);
index e0cabf7..f1fad69 100644 (file)
@@ -41,6 +41,7 @@
 #import "objc_instance.h"
 #import "runtime_root.h"
 #import <JavaScriptCore/APICast.h>
+#import <JavaScriptCore/JSContextInternal.h>
 #import <runtime/JSLock.h>
 
 #if ENABLE(NETSCAPE_PLUGIN_API)
@@ -109,6 +110,16 @@ WebScriptObject* ScriptController::windowScriptObject()
     return m_windowScriptObject.get();
 }
 
+#if JSC_OBJC_API_ENABLED
+JSContext *ScriptController::javaScriptContext()
+{
+    if (!canExecuteScripts(NotAboutToExecuteScript))
+        return 0;
+    JSContext *context = [JSContext contextWithGlobalContextRef:toGlobalRef(bindingRootObject()->globalObject()->globalExec())];
+    return context;
+}
+#endif
+
 void ScriptController::updatePlatformScriptObjects()
 {
     if (m_windowScriptObject) {
index f75f458..e7e0344 100644 (file)
@@ -23,6 +23,9 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
+#ifndef WebScriptObject_h
+#define WebScriptObject_h
+
 #import <Foundation/Foundation.h>
 #import <JavaScriptCore/JSBase.h>
 #import <JavaScriptCore/WebKitAvailability.h>
 
 // WebScriptObject --------------------------------------------------
 
+@class JSValue;
 @class WebScriptObjectPrivate;
 @class WebFrame;
 
 */
 - (void)setException:(NSString *)description;
 
+
+#if JSC_OBJC_API_ENABLED
+/*!
+    @method JSValue
+    @result The equivalent Objective-C JSValue for this WebScriptObject.
+    @discussion Use this method to bridge between the WebScriptObject and 
+    JavaScriptCore Objective-C APIs.
+*/
+- (JSValue *)JSValue;
+#endif
+
 @end
 
 
 @end
 
 #endif
+
+#endif // WebScriptObject_h
index a89eb96..8bcb63b 100644 (file)
@@ -43,6 +43,8 @@
 #import "runtime_object.h"
 #import "runtime_root.h"
 #import <JavaScriptCore/APICast.h>
+#import <JavaScriptCore/JSContextInternal.h>
+#import <JavaScriptCore/JSValueInternal.h>
 #import <interpreter/CallFrame.h>
 #import <runtime/InitializeThreading.h>
 #import <runtime/JSGlobalObject.h>
 #import <wtf/Threading.h>
 #include <wtf/text/WTFString.h>
 
-using namespace JSC;
 using namespace JSC::Bindings;
 using namespace WebCore;
 
+using JSC::CallData;
+using JSC::CallType;
+using JSC::CallTypeNone;
+using JSC::ExecState;
+using JSC::Identifier;
+using JSC::JSLockHolder;
+using JSC::JSObject;
+using JSC::MarkedArgumentBuffer;
+using JSC::PutPropertySlot;
+using JSC::jsCast;
+using JSC::jsUndefined;
+using JSC::makeSource;
+
 namespace WebCore {
 
 static NSMapTable* JSWrapperCache;
@@ -236,6 +250,13 @@ static void addExceptionToConsole(ExecState* exec)
     return BindingSecurity::shouldAllowAccessToDOMWindow(_private->originRootObject->globalObject()->globalExec(), target->impl());
 }
 
+- (JSGlobalContextRef)_globalContextRef
+{
+    if (![self _isSafeScript])
+        return nil;
+    return toGlobalRef([self _rootObject]->globalObject()->globalExec());
+}
+
 - (oneway void)release
 {
     // If we're releasing the last reference to this object, remove if from the map.
@@ -304,7 +325,7 @@ static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* root
     JSLockHolder lock(exec);
     ASSERT(!exec->hadException());
 
-    JSValue function = [self _imp]->get(exec, Identifier(exec, String(name)));
+    JSC::JSValue function = [self _imp]->get(exec, Identifier(exec, String(name)));
     CallData callData;
     CallType callType = getCallData(function, callData);
     if (callType == CallTypeNone)
@@ -317,7 +338,7 @@ static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* root
         return nil;
 
     [self _rootObject]->globalObject()->globalData().timeoutChecker.start();
-    JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, [self _imp], argList);
+    JSC::JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, [self _imp], argList);
     [self _rootObject]->globalObject()->globalData().timeoutChecker.stop();
 
     if (exec->hadException()) {
@@ -343,7 +364,7 @@ static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* root
     JSLockHolder lock(exec);
     
     [self _rootObject]->globalObject()->globalData().timeoutChecker.start();
-    JSValue returnValue = JSMainThreadExecState::evaluate(exec, makeSource(String(script)), JSC::JSValue(), 0);
+    JSC::JSValue returnValue = JSMainThreadExecState::evaluate(exec, makeSource(String(script)), JSC::JSValue(), 0);
     [self _rootObject]->globalObject()->globalData().timeoutChecker.stop();
 
     id resultObj = [WebScriptObject _convertValueToObjcValue:returnValue originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
@@ -385,7 +406,7 @@ static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* root
         // leaving the lock permanently held
         JSLockHolder lock(exec);
         
-        JSValue result = [self _imp]->get(exec, Identifier(exec, String(key)));
+        JSC::JSValue result = [self _imp]->get(exec, Identifier(exec, String(key)));
         
         if (exec->hadException()) {
             addExceptionToConsole(exec);
@@ -464,7 +485,7 @@ static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* root
     ASSERT(!exec->hadException());
 
     JSLockHolder lock(exec);
-    JSValue result = [self _imp]->get(exec, index);
+    JSC::JSValue result = [self _imp]->get(exec, index);
 
     if (exec->hadException()) {
         addExceptionToConsole(exec);
@@ -511,7 +532,7 @@ static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* root
     return toRef([self _imp]);
 }
 
-+ (id)_convertValueToObjcValue:(JSValue)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
++ (id)_convertValueToObjcValue:(JSC::JSValue)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
 {
     if (value.isObject()) {
         JSObject* object = asObject(value);
@@ -555,6 +576,18 @@ static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* root
     return nil;
 }
 
+
+#if JSC_OBJC_API_ENABLED
+- (JSValue *)JSValue
+{
+    if (![self _isSafeScript])
+        return 0;
+    
+    return [JSValue valueWithValue:[self JSObject] 
+                    inContext:[JSContext contextWithGlobalContextRef:[self _globalContextRef]]];
+}
+#endif
+
 @end
 
 @interface WebScriptObject (WebKitCocoaBindings)
index a286ac3..e7913a5 100644 (file)
@@ -57,6 +57,7 @@ namespace WebCore {
 - (BOOL)_hasImp;
 - (JSC::Bindings::RootObject*)_rootObject;
 - (JSC::Bindings::RootObject*)_originRootObject;
+- (JSGlobalContextRef)_globalContextRef;
 @end
 
 @interface WebScriptObject (StagedForPublic)
index 6259c37..988dd00 100644 (file)
@@ -1,3 +1,36 @@
+2013-02-19  Mark Hahnenberg  <mhahnenberg@apple.com>
+
+        Objective-C API: Need a way to use the Objective-C JavaScript API with WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=106059
+
+        Reviewed by Geoffrey Garen.
+
+        Addition of appropriate delegate callbacks and support to the WebKit API.
+
+        * WebCoreSupport/WebFrameLoaderClient.mm:
+        * WebView/WebDelegateImplementationCaching.h:
+        (WebFrameLoadDelegateImplementationCache):
+        * WebView/WebFrame.h:
+        * WebView/WebFrame.mm:
+        (-[WebFrame _stringByEvaluatingJavaScriptFromString:forceUserGesture:]):
+        (-[WebFrame _stringByEvaluatingJavaScriptFromString:withGlobalObject:inScriptWorld:]):
+        (-[WebFrame _javaScriptContextForScriptWorld:]):
+        (-[WebFrame javaScriptContext]):
+        * WebView/WebFrameLoadDelegate.h:
+        * WebView/WebFramePrivate.h:
+        * WebView/WebScriptDebugDelegate.mm:
+        (-[WebScriptCallFrame _convertValueToObjcValue:JSC::]):
+        (-[WebScriptCallFrame exception]):
+        (-[WebScriptCallFrame evaluateWebScript:]):
+        * WebView/WebScriptWorld.h:
+        * WebView/WebScriptWorld.mm:
+        (+[WebScriptWorld scriptWorldForJavaScriptContext:]):
+        * WebView/WebView.mm:
+        (-[WebView _cacheFrameLoadDelegateImplementations]):
+        (aeDescFromJSValue):
+        (-[WebView aeDescByEvaluatingJavaScriptFromString:]):
+        (-[WebView _computedStyleIncludingVisitedInfo:forElement:]):
+
 2013-02-20  Dirk Schulze  <krit@webkit.org>
 
         Enable CANVAS_PATH flag
index d82d0ec..e9ea8fc 100644 (file)
@@ -74,6 +74,7 @@
 #import "WebUIDelegate.h"
 #import "WebUIDelegatePrivate.h"
 #import "WebViewInternal.h"
+#import <JavaScriptCore/JSContextInternal.h>
 #import <WebCore/AuthenticationCF.h>
 #import <WebCore/AuthenticationMac.h>
 #import <WebCore/BackForwardController.h>
 #import <WebCore/ScriptController.h>
 #import <WebCore/SharedBuffer.h>
 #import <WebCore/WebCoreObjCExtras.h>
+#import <WebCore/WebScriptObjectPrivate.h>
 #import <WebCore/Widget.h>
 #import <WebKit/DOMElement.h>
 #import <WebKit/DOMHTMLFormElement.h>
@@ -1950,7 +1952,14 @@ void WebFrameLoaderClient::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*
     Frame *frame = core(m_webFrame.get());
     ScriptController *script = frame->script();
 
+#if JSC_OBJC_API_ENABLED
+    if (implementations->didCreateJavaScriptContextForFrameFunc) {
+        CallFrameLoadDelegate(implementations->didCreateJavaScriptContextForFrameFunc, webView, @selector(webView:didCreateJavaScriptContext:forFrame:),
+            script->javaScriptContext(), m_webFrame.get());
+    } else if (implementations->didClearWindowObjectForFrameFunc) {
+#else
     if (implementations->didClearWindowObjectForFrameFunc) {
+#endif
         CallFrameLoadDelegate(implementations->didClearWindowObjectForFrameFunc, webView, @selector(webView:didClearWindowObject:forFrame:),
             script->windowScriptObject(), m_webFrame.get());
     } else if (implementations->windowScriptObjectAvailableFunc) {
index c24d073..3909eaf 100644 (file)
@@ -52,6 +52,9 @@ struct WebResourceDelegateImplementationCache {
 };
 
 struct WebFrameLoadDelegateImplementationCache {
+#if JSC_OBJC_API_ENABLED
+    IMP didCreateJavaScriptContextForFrameFunc;
+#endif
     IMP didClearWindowObjectForFrameFunc;
     IMP didClearWindowObjectForFrameInScriptWorldFunc;
     IMP didClearInspectorWindowObjectForFrameFunc;
index 64015fd..d419890 100644 (file)
@@ -31,6 +31,7 @@
 
 @class DOMDocument;
 @class DOMHTMLElement;
+@class JSContext;
 @class NSURLRequest;
 @class WebArchive;
 @class WebDataSource;
     bridge between the WebKit and JavaScriptCore APIs.
 */
 - (JSGlobalContextRef)globalContext;
+
+#if JSC_OBJC_API_ENABLED
+/*!
+    @method javaScriptContext
+    @result The frame's global JavaScript execution context. Use this method to 
+    bridge between the WebKit and Objective-C JavaScriptCore API.
+*/
+- (JSContext *)javaScriptContext;
+#endif // JSC_OBJC_API_ENABLED
+
 @end
index 755c0d7..00d037a 100644 (file)
@@ -53,6 +53,7 @@
 #import "WebScriptWorldInternal.h"
 #import "WebViewInternal.h"
 #import <JavaScriptCore/APICast.h>
+#import <JavaScriptCore/JSContextInternal.h>
 #import <WebCore/AXObjectCache.h>
 #import <WebCore/AccessibilityObject.h>
 #import <WebCore/AnimationController.h>
@@ -106,7 +107,6 @@ using namespace HTMLNames;
 
 using JSC::JSGlobalObject;
 using JSC::JSLock;
-using JSC::JSValue;
 
 /*
 Here is the current behavior matrix for four types of navigations:
@@ -580,7 +580,7 @@ static inline WebDataSource *dataSource(DocumentLoader* loader)
     ASSERT(_private->coreFrame->document());
     RetainPtr<WebFrame> protect(self); // Executing arbitrary JavaScript can destroy the frame.
     
-    JSValue result = _private->coreFrame->script()->executeScript(string, forceUserGesture).jsValue();
+    JSC::JSValue result = _private->coreFrame->script()->executeScript(string, forceUserGesture).jsValue();
 
     if (!_private->coreFrame) // In case the script removed our frame from the page.
         return @"";
@@ -1059,7 +1059,7 @@ static inline WebDataSource *dataSource(DocumentLoader* loader)
     ASSERT(frame->document());
     RetainPtr<WebFrame> webFrame(kit(frame)); // Running arbitrary JavaScript can destroy the frame.
 
-    JSValue result = frame->script()->executeScriptInWorld(core(world), string, true).jsValue();
+    JSC::JSValue result = frame->script()->executeScriptInWorld(core(world), string, true).jsValue();
 
     if (!webFrame->_private->coreFrame) // In case the script removed our frame from the page.
         return @"";
@@ -1086,6 +1086,16 @@ static inline WebDataSource *dataSource(DocumentLoader* loader)
     return toGlobalRef(coreFrame->script()->globalObject(coreWorld)->globalExec());
 }
 
+#if JSC_OBJC_API_ENABLED
+- (JSContext *)_javaScriptContextForScriptWorld:(WebScriptWorld *)world
+{
+    JSGlobalContextRef globalContextRef = [self _globalContextForScriptWorld:world];
+    if (!globalContextRef)
+        return 0;
+    return [JSContext contextWithGlobalContextRef:globalContextRef];
+}
+#endif
+
 - (void)setAllowsScrollersToOverlapContent:(BOOL)flag
 {
     ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]);
@@ -1471,4 +1481,14 @@ static NSURL *createUniqueWebDataURL()
     return toGlobalRef(coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec());
 }
 
+#if JSC_OBJC_API_ENABLED
+- (JSContext *)javaScriptContext
+{
+    Frame* coreFrame = _private->coreFrame;
+    if (!coreFrame)
+        return 0;
+    return coreFrame->script()->javaScriptContext();
+}
+#endif
+
 @end
index e5f1c7c..a96c0c7 100644 (file)
 */
 - (void)webView:(WebView *)webView windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_WEBKIT_VERSION_1_3_AND_LATER_BUT_DEPRECATED_IN_WEBKIT_VERSION_3_0);
 
+#if JSC_OBJC_API_ENABLED
+/*!
+    @method webView:didCreateJavaScriptContext:contextForFrame:
+    @abstract Notifies the delegate that a new JavaScript context has been created created.
+    @param webView The WebView sending the message.
+    @param context The JSContext representing the frame's JavaScript window object.
+    @param frame The WebFrame to which the context belongs.
+    @discussion If a delegate implements webView:didCreateJavaScriptContext:forFrame: along with either 
+    webView:didClearWindowObject:forFrame: or webView:windowScriptObjectAvailable:, only 
+    webView:didCreateJavaScriptContext:forFrame will be invoked. This enables a delegate to implement 
+    multiple versions to maintain backwards compatibility with older versions of WebKit.
+*/
+- (void)webView:(WebView *)webView didCreateJavaScriptContext:(JSContext *)context forFrame:(WebFrame *)frame;
+#endif
+
 @end
index 6ee8233..d1fe592 100644 (file)
@@ -95,6 +95,10 @@ typedef enum {
 - (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string withGlobalObject:(JSObjectRef)globalObject inScriptWorld:(WebScriptWorld *)world;
 - (JSGlobalContextRef)_globalContextForScriptWorld:(WebScriptWorld *)world;
 
+#if JSC_OBJC_API_ENABLED
+- (JSContext *)_javaScriptContextForScriptWorld:(WebScriptWorld *)world;
+#endif
+
 - (void)_replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle;
 - (void)_replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace;
 - (void)_replaceSelectionWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace;
index a44ce23..0355af1 100644 (file)
@@ -55,7 +55,7 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
 
 @interface WebScriptCallFrame (WebScriptDebugDelegateInternal)
 
-- (id)_convertValueToObjcValue:(JSValue)value;
+- (id)_convertValueToObjcValue:(JSC::JSValue)value;
 
 @end
 
@@ -113,7 +113,7 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
     _private->debuggerCallFrame = 0;
 }
 
-- (id)_convertValueToObjcValue:(JSValue)value
+- (id)_convertValueToObjcValue:(JSC::JSValue)value
 {
     if (!value)
         return nil;
@@ -214,7 +214,7 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
     if (!_private->debuggerCallFrame)
         return nil;
 
-    JSValue exception = _private->debuggerCallFrame->exception();
+    JSC::JSValue exception = _private->debuggerCallFrame->exception();
     return exception ? [self _convertValueToObjcValue:exception] : nil;
 }
 
@@ -241,15 +241,15 @@ NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber";
 
         DynamicGlobalObjectScope globalObjectScope(globalObject->globalData(), globalObject);
 
-        JSValue exception;
-        JSValue result = evaluateInGlobalCallFrame(script, exception, globalObject);
+        JSC::JSValue exception;
+        JSC::JSValue result = evaluateInGlobalCallFrame(script, exception, globalObject);
         if (exception)
             return [self _convertValueToObjcValue:exception];
         return result ? [self _convertValueToObjcValue:result] : nil;        
     }
 
-    JSValue exception;
-    JSValue result = _private->debuggerCallFrame->evaluate(script, exception);
+    JSC::JSValue exception;
+    JSC::JSValue result = _private->debuggerCallFrame->evaluate(script, exception);
     if (exception)
         return [self _convertValueToObjcValue:exception];
     return result ? [self _convertValueToObjcValue:result] : nil;
index 9a05f7f..a394e0f 100644 (file)
@@ -22,8 +22,9 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-typedef struct OpaqueJSContext* JSGlobalContextRef;
+#include <JavaScriptCore/JSBase.h>
 
+@class JSContext;
 @class WebScriptWorldPrivate;
 
 @interface WebScriptWorld : NSObject {
@@ -35,6 +36,9 @@ typedef struct OpaqueJSContext* JSGlobalContextRef;
 + (WebScriptWorld *)world;
 
 + (WebScriptWorld *)scriptWorldForGlobalContext:(JSGlobalContextRef)globalContext;
+#if JSC_OBJC_API_ENABLED
++ (WebScriptWorld *)scriptWorldForJavaScriptContext:(JSContext *)context;
+#endif
 
 - (void)unregisterWorld;
 
index 61daff1..782cedc 100644 (file)
@@ -28,6 +28,7 @@
 #import <WebCore/JSDOMBinding.h>
 #import <WebCore/ScriptController.h>
 #import <JavaScriptCore/APICast.h>
+#import <JavaScriptCore/JSContextInternal.h>
 
 #import <wtf/RefPtr.h>
 
@@ -106,6 +107,13 @@ static WorldMap& allWorlds()
     return [self findOrCreateWorld:currentWorld(toJS(context))];
 }
 
+#if JSC_OBJC_API_ENABLED
++ (WebScriptWorld *)scriptWorldForJavaScriptContext:(JSContext *)context
+{
+    return [self scriptWorldForGlobalContext:contextInternalContext(context)];
+}
+#endif
+
 @end
 
 @implementation WebScriptWorld (WebInternal)
index bb50485..e960d2c 100644 (file)
@@ -1659,6 +1659,9 @@ static inline IMP getMethod(id o, SEL s)
     cache->didPushStateWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didPushStateWithinPageForFrame:));
     cache->didReplaceStateWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didReplaceStateWithinPageForFrame:));
     cache->didPopStateWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didPopStateWithinPageForFrame:));
+#if JSC_OBJC_API_ENABLED
+    cache->didCreateJavaScriptContextForFrameFunc = getMethod(delegate, @selector(webView:didCreateJavaScriptContext:forFrame:));
+#endif
     cache->didClearWindowObjectForFrameFunc = getMethod(delegate, @selector(webView:didClearWindowObject:forFrame:));
     cache->didClearWindowObjectForFrameInScriptWorldFunc = getMethod(delegate, @selector(webView:didClearWindowObjectForFrame:inScriptWorld:));
     cache->didClearInspectorWindowObjectForFrameFunc = getMethod(delegate, @selector(webView:didClearInspectorWindowObject:forFrame:));
@@ -4916,7 +4919,7 @@ static BOOL findString(NSView <WebDocumentSearching> *searchView, NSString *stri
     return coreFrame->loader()->shouldClose();
 }
 
-static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue jsValue)
+static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSC::JSValue jsValue)
 {
     NSAppleEventDescriptor* aeDesc = 0;
     if (jsValue.isBoolean())
@@ -4957,7 +4960,7 @@ static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue jsValu
                 return aeDesc;
             }
         }
-        JSValue primitive = object->toPrimitive(exec);
+        JSC::JSValue primitive = object->toPrimitive(exec);
         if (exec->hadException()) {
             exec->clearException();
             return [NSAppleEventDescriptor nullDescriptor];
@@ -4977,7 +4980,7 @@ static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue jsValu
         return nil;
     if (!coreFrame->document())
         return nil;
-    JSValue result = coreFrame->script()->executeScript(script, true).jsValue();
+    JSC::JSValue result = coreFrame->script()->executeScript(script, true).jsValue();
     if (!result) // FIXME: pass errors
         return 0;
     JSLockHolder lock(coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec());
@@ -6699,7 +6702,7 @@ static void glibContextIterationCallback(CFRunLoopObserverRef, CFRunLoopActivity
     JSLockHolder lock(exec);
     if (!value)
         return JSValueMakeUndefined(context);
-    JSValue jsValue = toJS(exec, value);
+    JSC::JSValue jsValue = toJS(exec, value);
     if (!jsValue.inherits(&JSElement::s_info))
         return JSValueMakeUndefined(context);
     JSElement* jsElement = static_cast<JSElement*>(asObject(jsValue));
index 6da6a99..b5e457b 100644 (file)
@@ -1,3 +1,24 @@
+2013-02-19  Mark Hahnenberg  <mhahnenberg@apple.com>
+
+        Objective-C API: Need a way to use the Objective-C JavaScript API with WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=106059
+
+        Reviewed by Geoffrey Garen.
+
+        Added new tests for the WebKit API portion of the JSC Objective-C API.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/mac/JSContextBackForwardCache1.html: Added.
+        * TestWebKitAPI/Tests/mac/JSContextBackForwardCache2.html: Added.
+        * TestWebKitAPI/Tests/mac/WebViewDidCreateJavaScriptContext.mm: Added.
+        (-[MyConsole log:]):
+        (-[MyConsole printHelloWorld]):
+        (-[MyConsole add:to:]):
+        (-[DidCreateJavaScriptContextFrameLoadDelegate webView:didFinishLoadForFrame:]):
+        (-[DidCreateJavaScriptContextFrameLoadDelegate webView:didCreateJavaScriptContext:forFrame:]):
+        (TestWebKitAPI):
+        (TestWebKitAPI::TEST):
+
 2013-02-21  Stephen Chenney  <schenney@chromium.org>
 
         Unreviewed, add myself as a reviewer
index 3e2bd02..d3733df 100644 (file)
                C0BD669F131D3CFF00E18F2A /* ResponsivenessTimerDoesntFireEarly_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0BD669E131D3CFF00E18F2A /* ResponsivenessTimerDoesntFireEarly_Bundle.cpp */; };
                C0C5D3BE14598B6F00A802A6 /* GetBackingScaleFactor.mm in Sources */ = {isa = PBXBuildFile; fileRef = C0C5D3BC14598B6F00A802A6 /* GetBackingScaleFactor.mm */; };
                C0C5D3C61459912900A802A6 /* GetBackingScaleFactor_Bundle.mm in Sources */ = {isa = PBXBuildFile; fileRef = C0C5D3BD14598B6F00A802A6 /* GetBackingScaleFactor_Bundle.mm */; };
+               C2CF975A16CEC7140054E99D /* JSContextBackForwardCache2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = C2CF975916CEC69E0054E99D /* JSContextBackForwardCache2.html */; };
+               C2CF975B16CEC71B0054E99D /* JSContextBackForwardCache1.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = C2CF975816CEC69E0054E99D /* JSContextBackForwardCache1.html */; };
+               C2EB2DD316CAC7AC009B52EE /* WebViewDidCreateJavaScriptContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = C2EB2DD116CAC7AC009B52EE /* WebViewDidCreateJavaScriptContext.mm */; };
                C507E8A714C6545B005D6B3B /* InspectorBar.mm in Sources */ = {isa = PBXBuildFile; fileRef = C507E8A614C6545B005D6B3B /* InspectorBar.mm */; };
                C51AFB99169F49FF009CCF66 /* FindMatches.mm in Sources */ = {isa = PBXBuildFile; fileRef = C51AFB98169F49FF009CCF66 /* FindMatches.mm */; };
                C540F776152E4DA000A40C8C /* SimplifyMarkup.mm in Sources */ = {isa = PBXBuildFile; fileRef = C540F775152E4DA000A40C8C /* SimplifyMarkup.mm */; };
                        dstPath = TestWebKitAPI.resources;
                        dstSubfolderSpec = 7;
                        files = (
+                               C2CF975B16CEC71B0054E99D /* JSContextBackForwardCache1.html in Copy Resources */,
+                               C2CF975A16CEC7140054E99D /* JSContextBackForwardCache2.html in Copy Resources */,
                                1A9E52C913E65EF4006917F5 /* 18-characters.html in Copy Resources */,
                                379028B914FAC24C007E6B43 /* acceptsFirstMouse.html in Copy Resources */,
                                B55F11BE15191A0600915916 /* Ahem.ttf in Copy Resources */,
                C0BD669E131D3CFF00E18F2A /* ResponsivenessTimerDoesntFireEarly_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResponsivenessTimerDoesntFireEarly_Bundle.cpp; sourceTree = "<group>"; };
                C0C5D3BC14598B6F00A802A6 /* GetBackingScaleFactor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GetBackingScaleFactor.mm; sourceTree = "<group>"; };
                C0C5D3BD14598B6F00A802A6 /* GetBackingScaleFactor_Bundle.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GetBackingScaleFactor_Bundle.mm; sourceTree = "<group>"; };
+               C2CF975816CEC69E0054E99D /* JSContextBackForwardCache1.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = JSContextBackForwardCache1.html; sourceTree = "<group>"; };
+               C2CF975916CEC69E0054E99D /* JSContextBackForwardCache2.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = JSContextBackForwardCache2.html; sourceTree = "<group>"; };
+               C2EB2DD116CAC7AC009B52EE /* WebViewDidCreateJavaScriptContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebViewDidCreateJavaScriptContext.mm; sourceTree = "<group>"; };
                C507E8A614C6545B005D6B3B /* InspectorBar.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = InspectorBar.mm; sourceTree = "<group>"; };
                C51AFB98169F49FF009CCF66 /* FindMatches.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FindMatches.mm; sourceTree = "<group>"; };
                C540F775152E4DA000A40C8C /* SimplifyMarkup.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SimplifyMarkup.mm; sourceTree = "<group>"; };
                                51FBBB4C1513D4E900822738 /* WebViewCanPasteURL.mm */,
                                37E38C33169B7D010084C28C /* WebViewDidRemoveFrameFromHierarchy.mm */,
                                A5E2027215B2181900C13E14 /* WindowlessWebViewWithMedia.mm */,
+                               C2EB2DD116CAC7AC009B52EE /* WebViewDidCreateJavaScriptContext.mm */,
                        );
                        path = mac;
                        sourceTree = "<group>";
                C07E6CB013FD737C0038B22B /* Resources */ = {
                        isa = PBXGroup;
                        children = (
+                               C2CF975816CEC69E0054E99D /* JSContextBackForwardCache1.html */,
+                               C2CF975916CEC69E0054E99D /* JSContextBackForwardCache2.html */,
                                379028B814FABE49007E6B43 /* acceptsFirstMouse.html */,
                                B55F11B9151916E600915916 /* Ahem.ttf */,
                                B55F11B01517A2C400915916 /* attributedStringCustomFont.html */,
 /* Begin PBXProject section */
                08FB7793FE84155DC02AAC07 /* Project object */ = {
                        isa = PBXProject;
+                       attributes = {
+                       };
                        buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "TestWebKitAPI" */;
                        compatibilityVersion = "Xcode 3.1";
                        developmentRegion = English;
                                4BFDFFA9131477770061F24B /* HitTestResultNodeHandle.cpp in Sources */,
                                9B4F8FA4159D52B1002D9F94 /* HTMLCollectionNamedItem.mm in Sources */,
                                9B26FC6C159D061000CC3765 /* HTMLFormCollectionNamedItem.mm in Sources */,
+                               C2EB2DD316CAC7AC009B52EE /* WebViewDidCreateJavaScriptContext.mm in Sources */,
                                BC575AAD126E83B9006F0F12 /* InjectedBundleBasic.cpp in Sources */,
                                378E64731632646D00B6C676 /* InjectedBundleFrameHitTest.cpp in Sources */,
                                F660AA1315A619C9003A1243 /* InjectedBundleInitializationUserDataCallbackWins.cpp in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/mac/JSContextBackForwardCache1.html b/Tools/TestWebKitAPI/Tests/mac/JSContextBackForwardCache1.html
new file mode 100644 (file)
index 0000000..f736a78
--- /dev/null
@@ -0,0 +1,10 @@
+<body>
+    <div id="test-div"></div>
+    <script>
+        var testDiv = document.getElementById("test-div");
+        if (!testDiv.myCustomProperty)
+            insertMyCustomProperty(testDiv);
+        else
+            checkForMyCustomProperty(testDiv);
+    </script>
+</body>
diff --git a/Tools/TestWebKitAPI/Tests/mac/JSContextBackForwardCache2.html b/Tools/TestWebKitAPI/Tests/mac/JSContextBackForwardCache2.html
new file mode 100644 (file)
index 0000000..1d26d21
--- /dev/null
@@ -0,0 +1,10 @@
+<body>
+    <div id="test-div"></div>
+    <script>
+        var testDiv = document.getElementById("test-div");
+        if (testDiv.myCustomProperty)
+            myConsole.log("ERROR: found myCustomProperty.");
+        else
+            didCompleteTestSuccessfully();
+    </script>
+</body>
diff --git a/Tools/TestWebKitAPI/Tests/mac/WebViewDidCreateJavaScriptContext.mm b/Tools/TestWebKitAPI/Tests/mac/WebViewDidCreateJavaScriptContext.mm
new file mode 100644 (file)
index 0000000..39504dd
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "PlatformUtilities.h"
+#import <JavaScriptCore/JSExport.h>
+#import <JavaScriptCore/JSContext.h>
+#import <WebKit/WebFrameLoadDelegatePrivate.h>
+#import <wtf/RetainPtr.h>
+
+#if JSC_OBJC_API_ENABLED
+
+@class MyConsole;
+
+static bool didFinishLoad = false;
+static bool didCompleteTestSuccessfully = false;
+static bool didCallWindowCallback = false;
+static bool didFindMyCustomProperty = false;
+static bool didInsertMyCustomProperty = true;
+
+@protocol MyConsole<JSExport>
+- (void)log:(NSString *)s;
+- (void)printHelloWorld;
+- (int)add:(int)a to:(int)b;
+@end
+
+@interface MyConsole : NSObject<MyConsole>
+@end
+
+@implementation MyConsole
+- (void)log:(NSString *)s
+{
+    NSLog(@"%@", s);
+}
+
+- (void)printHelloWorld
+{
+    NSLog(@"Hello, World!");
+}
+
+- (int)add:(int)a to:(int)b
+{
+    return a + b;
+}
+@end
+
+@interface DidCreateJavaScriptContextFrameLoadDelegate : NSObject
+@end
+
+@implementation DidCreateJavaScriptContextFrameLoadDelegate
+
+- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
+{
+    didFinishLoad = true;
+}
+
+- (void)webView:(WebView *)webView didCreateJavaScriptContext:(JSContext *)context forFrame:(WebFrame *)frame
+{
+    MyConsole *myConsole = [[MyConsole alloc] init];
+    context[@"myConsole"] = myConsole;
+    context.exceptionHandler = nil;
+    [myConsole release];
+
+    context[@"windowCallback"] = ^(JSValue *thisObject){
+        didCallWindowCallback = true;
+    };
+
+    context[@"didCompleteTestSuccessfully"] = ^{
+        didCompleteTestSuccessfully = true;
+    };
+
+    context[@"callMeBack"] = ^(JSValue *functionValue) {
+        [functionValue callWithArguments:[NSArray array]];
+    };
+
+    context[@"checkForMyCustomProperty"] = ^(JSValue *element) {
+        if ([element hasProperty:@"myCustomProperty"] && [[element valueForProperty:@"myCustomProperty"] toInt32] == 42)
+            didFindMyCustomProperty = true;
+        else
+            NSLog(@"ERROR: Did not find myCustomProperty.");
+    };
+
+    context[@"insertMyCustomProperty"] = ^(JSValue *element) {
+        JSValue *fortyTwo = [JSValue valueWithInt32:42 inContext:[JSContext currentContext]];
+        [element setValue:fortyTwo forProperty:@"myCustomProperty"];
+        didInsertMyCustomProperty = true;
+    };
+}
+
+@end
+
+namespace TestWebKitAPI {
+
+TEST(WebKit1, DidCreateJavaScriptContextSanity1)
+{
+    didFinishLoad = false;
+    @autoreleasepool {
+        RetainPtr<WebView> webView = adoptNS([[WebView alloc] initWithFrame:NSMakeRect(0, 0, 120, 200) frameName:nil groupName:nil]);
+        RetainPtr<DidCreateJavaScriptContextFrameLoadDelegate> frameLoadDelegate = adoptNS([[DidCreateJavaScriptContextFrameLoadDelegate alloc] init]);
+
+        webView.get().frameLoadDelegate = frameLoadDelegate.get();
+        WebFrame *mainFrame = webView.get().mainFrame;
+
+        NSString *bodyString = 
+            @"<body> \
+                <script> \
+                    myConsole.printHelloWorld(); \
+                    myConsole.log(\"Loaded custom stuff.\"); \
+                    myConsole.log(myConsole.addTo(40, 2)); \
+                    didCompleteTestSuccessfully(); \
+                </script> \
+            </body>";
+        NSURL *aboutBlankURL = [NSURL URLWithString:@"about:blank"];
+
+        [mainFrame loadHTMLString:bodyString baseURL:aboutBlankURL];
+        Util::run(&didCompleteTestSuccessfully);
+    }
+}
+
+TEST(WebKit1, DidCreateJavaScriptContextSanity2)
+{
+    didCallWindowCallback = false;
+    @autoreleasepool {
+        RetainPtr<WebView> webView = adoptNS([[WebView alloc] initWithFrame:NSMakeRect(0, 0, 120, 200) frameName:nil groupName:nil]);
+        RetainPtr<DidCreateJavaScriptContextFrameLoadDelegate> frameLoadDelegate = adoptNS([[DidCreateJavaScriptContextFrameLoadDelegate alloc] init]);
+
+        webView.get().frameLoadDelegate = frameLoadDelegate.get();
+        WebFrame *mainFrame = webView.get().mainFrame;
+
+        NSString *bodyString = 
+            @"<body> \
+                <script> \
+                    setTimeout(windowCallback, 100); \
+                </script> \
+            </body>";
+        NSURL *aboutBlankURL = [NSURL URLWithString:@"about:blank"];
+
+        [mainFrame loadHTMLString:bodyString baseURL:aboutBlankURL];
+        Util::run(&didCallWindowCallback);
+    }
+}
+
+TEST(WebKit1, DidCreateJavaScriptContextCallJSFunctionFromObjCCallbackTest)
+{
+    @autoreleasepool {
+        RetainPtr<WebView> webView = adoptNS([[WebView alloc] initWithFrame:NSMakeRect(0, 0, 120, 200) frameName:nil groupName:nil]);
+        RetainPtr<DidCreateJavaScriptContextFrameLoadDelegate> frameLoadDelegate = adoptNS([[DidCreateJavaScriptContextFrameLoadDelegate alloc] init]);
+
+        webView.get().frameLoadDelegate = frameLoadDelegate.get();
+        WebFrame *mainFrame = webView.get().mainFrame;
+
+        NSString *bodyString = 
+            @"<body> \
+                <script> \
+                    callMeBack(function() { \
+                        didCompleteTestSuccessfully(); \
+                    }); \
+                </script> \
+            </body>";
+        NSURL *aboutBlankURL = [NSURL URLWithString:@"about:blank"];
+
+        [mainFrame loadHTMLString:bodyString baseURL:aboutBlankURL];
+        Util::run(&didCompleteTestSuccessfully);
+    }
+}
+
+TEST(WebKit1, DidCreateJavaScriptContextAddCustomPropertiesFromJSTest)
+{
+    didFindMyCustomProperty = false;
+    @autoreleasepool {
+        RetainPtr<WebView> webView = adoptNS([[WebView alloc] initWithFrame:NSMakeRect(0, 0, 120, 200) frameName:nil groupName:nil]);
+        RetainPtr<DidCreateJavaScriptContextFrameLoadDelegate> frameLoadDelegate = adoptNS([[DidCreateJavaScriptContextFrameLoadDelegate alloc] init]);
+
+        webView.get().frameLoadDelegate = frameLoadDelegate.get();
+        WebFrame *mainFrame = webView.get().mainFrame;
+
+        NSString *bodyString = 
+        @"<body> \
+            <div id=\"test-div\"></div> \
+            <script> \
+                var testDiv = document.getElementById(\"test-div\"); \
+                testDiv.myCustomProperty = 42; \
+                checkForMyCustomProperty(testDiv); \
+            </script> \
+        </body>";
+        NSURL *aboutBlankURL = [NSURL URLWithString:@"about:blank"];
+
+        [mainFrame loadHTMLString:bodyString baseURL:aboutBlankURL];
+        Util::run(&didFindMyCustomProperty);
+    }
+}
+
+TEST(WebKit1, DidCreateJavaScriptContextAddCustomPropertiesFromObjCTest)
+{
+    didFindMyCustomProperty = false;
+    @autoreleasepool {
+        RetainPtr<WebView> webView = adoptNS([[WebView alloc] initWithFrame:NSMakeRect(0, 0, 120, 200) frameName:nil groupName:nil]);
+        RetainPtr<DidCreateJavaScriptContextFrameLoadDelegate> frameLoadDelegate = adoptNS([[DidCreateJavaScriptContextFrameLoadDelegate alloc] init]);
+
+        webView.get().frameLoadDelegate = frameLoadDelegate.get();
+        WebFrame *mainFrame = webView.get().mainFrame;
+
+        NSString *bodyString = 
+            @"<body> \
+                <div id=\"test-div\"></div> \
+                <script> \
+                    var testDiv = document.getElementById(\"test-div\"); \
+                    insertMyCustomProperty(testDiv); \
+                    if (testDiv.myCustomProperty === 42) { \
+                        checkForMyCustomProperty(testDiv); \
+                    } \
+                </script> \
+            </body>";
+        NSURL *aboutBlankURL = [NSURL URLWithString:@"about:blank"];
+
+        [mainFrame loadHTMLString:bodyString baseURL:aboutBlankURL];
+        Util::run(&didFindMyCustomProperty);
+    }
+}
+
+TEST(WebKit1, DidCreateJavaScriptContextBackForwardCacheTest)
+{
+    didInsertMyCustomProperty = false;
+    didFindMyCustomProperty = false;
+    didCompleteTestSuccessfully = false;
+    @autoreleasepool {
+        RetainPtr<WebView> webView = adoptNS([[WebView alloc] initWithFrame:NSMakeRect(0, 0, 120, 200) frameName:nil groupName:nil]);
+        RetainPtr<DidCreateJavaScriptContextFrameLoadDelegate> frameLoadDelegate = adoptNS([[DidCreateJavaScriptContextFrameLoadDelegate alloc] init]);
+
+        webView.get().frameLoadDelegate = frameLoadDelegate.get();
+        WebFrame *mainFrame = webView.get().mainFrame;
+
+        NSURL *url1 = [[NSBundle mainBundle] URLForResource:@"JSContextBackForwardCache1" 
+                                              withExtension:@"html" 
+                                               subdirectory:@"TestWebKitAPI.resources"];
+        [mainFrame loadRequest:[NSURLRequest requestWithURL:url1]];
+        Util::run(&didInsertMyCustomProperty);
+
+        NSURL *url2 = [[NSBundle mainBundle] URLForResource:@"JSContextBackForwardCache2" 
+                                              withExtension:@"html" 
+                                               subdirectory:@"TestWebKitAPI.resources"];
+        [mainFrame loadRequest:[NSURLRequest requestWithURL:url2]];
+        Util::run(&didCompleteTestSuccessfully);
+
+        didCompleteTestSuccessfully = false;
+        [[mainFrame javaScriptContext] evaluateScript:
+            @"var testDiv = document.getElementById(\"test-div\"); \
+            if (!testDiv.myCustomProperty) { \
+                didCompleteTestSuccessfully(); \
+            }"];
+        EXPECT_TRUE(didCompleteTestSuccessfully);
+
+        if ([webView.get() goBack]) {
+            [[mainFrame javaScriptContext] evaluateScript:
+                @"var testDiv = document.getElementById(\"test-div\"); \
+                checkForMyCustomProperty(testDiv);"];
+            EXPECT_TRUE(didFindMyCustomProperty);
+        } else
+            EXPECT_TRUE(false);
+    }
+}
+
+} // namespace TestWebKitAPI
+
+#endif // ENABLE(JSC_OBJC_API)