summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/bindings/objc/WebScriptObject.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/bindings/objc/WebScriptObject.mm')
-rw-r--r--Source/WebCore/bindings/objc/WebScriptObject.mm678
1 files changed, 678 insertions, 0 deletions
diff --git a/Source/WebCore/bindings/objc/WebScriptObject.mm b/Source/WebCore/bindings/objc/WebScriptObject.mm
new file mode 100644
index 0000000..6bf7afe
--- /dev/null
+++ b/Source/WebCore/bindings/objc/WebScriptObject.mm
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2004, 2006, 2007, 2008 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 COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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 "WebScriptObjectPrivate.h"
+
+#import "Bridge.h"
+#import "Console.h"
+#import "DOMInternal.h"
+#import "DOMWindow.h"
+#import "Frame.h"
+#import "JSDOMWindow.h"
+#import "JSDOMWindowCustom.h"
+#import "JSHTMLElement.h"
+#import "JSMainThreadExecState.h"
+#import "JSPluginElementFunctions.h"
+#import "ObjCRuntimeObject.h"
+#import "PlatformString.h"
+#import "StringSourceProvider.h"
+#import "WebCoreObjCExtras.h"
+#import "objc_instance.h"
+#import "runtime_object.h"
+#import "runtime_root.h"
+#import <JavaScriptCore/APICast.h>
+#import <interpreter/CallFrame.h>
+#import <runtime/InitializeThreading.h>
+#import <runtime/JSGlobalObject.h>
+#import <runtime/JSLock.h>
+#import <runtime/Completion.h>
+#import <runtime/Completion.h>
+#import <wtf/Threading.h>
+
+#ifdef BUILDING_ON_TIGER
+typedef unsigned NSUInteger;
+#endif
+
+using namespace JSC;
+using namespace JSC::Bindings;
+using namespace WebCore;
+
+namespace WebCore {
+
+static NSMapTable* JSWrapperCache;
+
+NSObject* getJSWrapper(JSObject* impl)
+{
+ if (!JSWrapperCache)
+ return nil;
+ return static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
+}
+
+void addJSWrapper(NSObject* wrapper, JSObject* impl)
+{
+ if (!JSWrapperCache)
+ JSWrapperCache = createWrapperCache();
+ NSMapInsert(JSWrapperCache, impl, wrapper);
+}
+
+void removeJSWrapper(JSObject* impl)
+{
+ if (!JSWrapperCache)
+ return;
+ NSMapRemove(JSWrapperCache, impl);
+}
+
+id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root)
+{
+ if (id wrapper = getJSWrapper(object))
+ return [[wrapper retain] autorelease];
+ return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
+}
+
+static void addExceptionToConsole(ExecState* exec)
+{
+ JSDOMWindow* window = asJSDOMWindow(exec->dynamicGlobalObject());
+ if (!window || !exec->hadException())
+ return;
+ reportCurrentException(exec);
+}
+
+} // namespace WebCore
+
+@implementation WebScriptObjectPrivate
+
+@end
+
+@implementation WebScriptObject
+
++ (void)initialize
+{
+ JSC::initializeThreading();
+ WTF::initializeMainThreadToProcessMainThread();
+#ifndef BUILDING_ON_TIGER
+ WebCoreObjCFinalizeOnMainThread(self);
+#endif
+}
+
++ (id)scriptObjectForJSObject:(JSObjectRef)jsObject originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
+{
+ if (id domWrapper = createDOMWrapper(toJS(jsObject), originRootObject, rootObject))
+ return domWrapper;
+
+ return WebCore::createJSWrapper(toJS(jsObject), originRootObject, rootObject);
+}
+
+static void _didExecute(WebScriptObject *obj)
+{
+ ASSERT(JSLock::lockCount() > 0);
+
+ RootObject* root = [obj _rootObject];
+ if (!root)
+ return;
+
+ ExecState* exec = root->globalObject()->globalExec();
+ KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction();
+ if (func)
+ func(exec, root->globalObject());
+}
+
+- (void)_setImp:(JSObject*)imp originRootObject:(PassRefPtr<RootObject>)originRootObject rootObject:(PassRefPtr<RootObject>)rootObject
+{
+ // This function should only be called once, as a (possibly lazy) initializer.
+ ASSERT(!_private->imp);
+ ASSERT(!_private->rootObject);
+ ASSERT(!_private->originRootObject);
+ ASSERT(imp);
+
+ _private->imp = imp;
+ _private->rootObject = rootObject.releaseRef();
+ _private->originRootObject = originRootObject.releaseRef();
+
+ WebCore::addJSWrapper(self, imp);
+
+ if (_private->rootObject)
+ _private->rootObject->gcProtect(imp);
+}
+
+- (void)_setOriginRootObject:(PassRefPtr<RootObject>)originRootObject andRootObject:(PassRefPtr<RootObject>)rootObject
+{
+ ASSERT(_private->imp);
+
+ if (rootObject)
+ rootObject->gcProtect(_private->imp);
+
+ if (_private->rootObject && _private->rootObject->isValid())
+ _private->rootObject->gcUnprotect(_private->imp);
+
+ if (_private->rootObject)
+ _private->rootObject->deref();
+
+ if (_private->originRootObject)
+ _private->originRootObject->deref();
+
+ _private->rootObject = rootObject.releaseRef();
+ _private->originRootObject = originRootObject.releaseRef();
+}
+
+- (id)_initWithJSObject:(JSC::JSObject*)imp originRootObject:(PassRefPtr<JSC::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<JSC::Bindings::RootObject>)rootObject
+{
+ ASSERT(imp);
+
+ self = [super init];
+ _private = [[WebScriptObjectPrivate alloc] init];
+ [self _setImp:imp originRootObject:originRootObject rootObject:rootObject];
+
+ return self;
+}
+
+- (JSObject*)_imp
+{
+ // Associate the WebScriptObject with the JS wrapper for the ObjC DOM wrapper.
+ // This is done on lazily, on demand.
+ if (!_private->imp && _private->isCreatedByDOMWrapper)
+ [self _initializeScriptDOMNodeImp];
+ return [self _rootObject] ? _private->imp : 0;
+}
+
+- (BOOL)_hasImp
+{
+ return _private->imp != nil;
+}
+
+// Node that DOMNode overrides this method. So you should almost always
+// use this method call instead of _private->rootObject directly.
+- (RootObject*)_rootObject
+{
+ return _private->rootObject && _private->rootObject->isValid() ? _private->rootObject : 0;
+}
+
+- (RootObject *)_originRootObject
+{
+ return _private->originRootObject && _private->originRootObject->isValid() ? _private->originRootObject : 0;
+}
+
+- (BOOL)_isSafeScript
+{
+ RootObject *root = [self _rootObject];
+ if (!root)
+ return false;
+
+ if (!_private->originRootObject)
+ return true;
+
+ if (!_private->originRootObject->isValid())
+ return false;
+
+ return root->globalObject()->allowsAccessFrom(_private->originRootObject->globalObject());
+}
+
+- (void)dealloc
+{
+ if (WebCoreObjCScheduleDeallocateOnMainThread([WebScriptObject class], self))
+ return;
+
+ if (_private->imp)
+ WebCore::removeJSWrapper(_private->imp);
+
+ if (_private->rootObject && _private->rootObject->isValid())
+ _private->rootObject->gcUnprotect(_private->imp);
+
+ if (_private->rootObject)
+ _private->rootObject->deref();
+
+ if (_private->originRootObject)
+ _private->originRootObject->deref();
+
+ [_private release];
+
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ if (_private->rootObject && _private->rootObject->isValid())
+ _private->rootObject->gcUnprotect(_private->imp);
+
+ if (_private->rootObject)
+ _private->rootObject->deref();
+
+ if (_private->originRootObject)
+ _private->originRootObject->deref();
+
+ [super finalize];
+}
+
++ (BOOL)throwException:(NSString *)exceptionMessage
+{
+ ObjcInstance::setGlobalException(exceptionMessage);
+ return YES;
+}
+
+static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* rootObject, MarkedArgumentBuffer& aList)
+{
+ int i, numObjects = array ? [array count] : 0;
+
+ for (i = 0; i < numObjects; i++) {
+ id anObject = [array objectAtIndex:i];
+ aList.append(convertObjcValueToValue(exec, &anObject, ObjcObjectType, rootObject));
+ }
+}
+
+- (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args
+{
+ if (![self _isSafeScript])
+ return nil;
+
+ JSLock lock(SilenceAssertionsOnly);
+
+ // Look up the function object.
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+ ASSERT(!exec->hadException());
+
+ JSValue function = [self _imp]->get(exec, Identifier(exec, stringToUString(String(name))));
+ CallData callData;
+ CallType callType = getCallData(function, callData);
+ if (callType == CallTypeNone)
+ return nil;
+
+ MarkedArgumentBuffer argList;
+ getListFromNSArray(exec, args, [self _rootObject], argList);
+
+ if (![self _isSafeScript])
+ return nil;
+
+ [self _rootObject]->globalObject()->globalData().timeoutChecker.start();
+ JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, [self _imp], argList);
+ [self _rootObject]->globalObject()->globalData().timeoutChecker.stop();
+
+ if (exec->hadException()) {
+ addExceptionToConsole(exec);
+ result = jsUndefined();
+ exec->clearException();
+ }
+
+ // Convert and return the result of the function call.
+ id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
+
+ _didExecute(self);
+
+ return resultObj;
+}
+
+- (id)evaluateWebScript:(NSString *)script
+{
+ if (![self _isSafeScript])
+ return nil;
+
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+ ASSERT(!exec->hadException());
+
+ JSValue result;
+ JSLock lock(SilenceAssertionsOnly);
+
+ [self _rootObject]->globalObject()->globalData().timeoutChecker.start();
+ Completion completion = JSMainThreadExecState::evaluate([self _rootObject]->globalObject()->globalExec(), [self _rootObject]->globalObject()->globalScopeChain(), makeSource(String(script)), JSC::JSValue());
+ [self _rootObject]->globalObject()->globalData().timeoutChecker.stop();
+ ComplType type = completion.complType();
+
+ if (type == Normal) {
+ result = completion.value();
+ if (!result)
+ result = jsUndefined();
+ } else
+ result = jsUndefined();
+
+ if (exec->hadException()) {
+ addExceptionToConsole(exec);
+ result = jsUndefined();
+ exec->clearException();
+ }
+
+ id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
+
+ _didExecute(self);
+
+ return resultObj;
+}
+
+- (void)setValue:(id)value forKey:(NSString *)key
+{
+ if (![self _isSafeScript])
+ return;
+
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+ ASSERT(!exec->hadException());
+
+ JSLock lock(SilenceAssertionsOnly);
+
+ PutPropertySlot slot;
+ [self _imp]->put(exec, Identifier(exec, stringToUString(String(key))), convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]), slot);
+
+ if (exec->hadException()) {
+ addExceptionToConsole(exec);
+ exec->clearException();
+ }
+
+ _didExecute(self);
+}
+
+- (id)valueForKey:(NSString *)key
+{
+ if (![self _isSafeScript])
+ return nil;
+
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+ ASSERT(!exec->hadException());
+
+ id resultObj;
+ {
+ // Need to scope this lock to ensure that we release the lock before calling
+ // [super valueForKey:key] which might throw an exception and bypass the JSLock destructor,
+ // leaving the lock permanently held
+ JSLock lock(SilenceAssertionsOnly);
+
+ JSValue result = [self _imp]->get(exec, Identifier(exec, stringToUString(String(key))));
+
+ if (exec->hadException()) {
+ addExceptionToConsole(exec);
+ result = jsUndefined();
+ exec->clearException();
+ }
+
+ resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
+ }
+
+ if ([resultObj isKindOfClass:[WebUndefined class]])
+ resultObj = [super valueForKey:key]; // defaults to throwing an exception
+
+ JSLock lock(SilenceAssertionsOnly);
+ _didExecute(self);
+
+ return resultObj;
+}
+
+- (void)removeWebScriptKey:(NSString *)key
+{
+ if (![self _isSafeScript])
+ return;
+
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+ ASSERT(!exec->hadException());
+
+ JSLock lock(SilenceAssertionsOnly);
+ [self _imp]->deleteProperty(exec, Identifier(exec, stringToUString(String(key))));
+
+ if (exec->hadException()) {
+ addExceptionToConsole(exec);
+ exec->clearException();
+ }
+
+ _didExecute(self);
+}
+
+- (BOOL)hasWebScriptKey:(NSString *)key
+{
+ if (![self _isSafeScript])
+ return NO;
+
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+ ASSERT(!exec->hadException());
+
+ JSLock lock(SilenceAssertionsOnly);
+ BOOL result = [self _imp]->hasProperty(exec, Identifier(exec, stringToUString(String(key))));
+
+ if (exec->hadException()) {
+ addExceptionToConsole(exec);
+ exec->clearException();
+ }
+
+ _didExecute(self);
+
+ return result;
+}
+
+- (NSString *)stringRepresentation
+{
+ if (![self _isSafeScript]) {
+ // This is a workaround for a gcc 3.3 internal compiler error.
+ return @"Undefined";
+ }
+
+ JSLock lock(SilenceAssertionsOnly);
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+
+ id result = convertValueToObjcValue(exec, [self _imp], ObjcObjectType).objectValue;
+
+ NSString *description = [result description];
+
+ _didExecute(self);
+
+ return description;
+}
+
+- (id)webScriptValueAtIndex:(unsigned)index
+{
+ if (![self _isSafeScript])
+ return nil;
+
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+ ASSERT(!exec->hadException());
+
+ JSLock lock(SilenceAssertionsOnly);
+ JSValue result = [self _imp]->get(exec, index);
+
+ if (exec->hadException()) {
+ addExceptionToConsole(exec);
+ result = jsUndefined();
+ exec->clearException();
+ }
+
+ id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
+
+ _didExecute(self);
+
+ return resultObj;
+}
+
+- (void)setWebScriptValueAtIndex:(unsigned)index value:(id)value
+{
+ if (![self _isSafeScript])
+ return;
+
+ ExecState* exec = [self _rootObject]->globalObject()->globalExec();
+ ASSERT(!exec->hadException());
+
+ JSLock lock(SilenceAssertionsOnly);
+ [self _imp]->put(exec, index, convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]));
+
+ if (exec->hadException()) {
+ addExceptionToConsole(exec);
+ exec->clearException();
+ }
+
+ _didExecute(self);
+}
+
+- (void)setException:(NSString *)description
+{
+ if (![self _rootObject])
+ return;
+ ObjcInstance::setGlobalException(description, [self _rootObject]->globalObject());
+}
+
+- (JSObjectRef)JSObject
+{
+ if (![self _isSafeScript])
+ return NULL;
+
+ return toRef([self _imp]);
+}
+
++ (id)_convertValueToObjcValue:(JSValue)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
+{
+ if (value.isObject()) {
+ JSObject* object = asObject(value);
+ JSLock lock(SilenceAssertionsOnly);
+
+ if (object->inherits(&JSHTMLElement::s_info)) {
+ // Plugin elements cache the instance internally.
+ HTMLElement* el = static_cast<JSHTMLElement*>(object)->impl();
+ ObjcInstance* instance = static_cast<ObjcInstance*>(pluginInstance(el));
+ if (instance)
+ return instance->getObject();
+ } else if (object->inherits(&ObjCRuntimeObject::s_info)) {
+ ObjCRuntimeObject* runtimeObject = static_cast<ObjCRuntimeObject*>(object);
+ ObjcInstance* instance = runtimeObject->getInternalObjCInstance();
+ if (instance)
+ return instance->getObject();
+ return nil;
+ }
+
+ return [WebScriptObject scriptObjectForJSObject:toRef(object) originRootObject:originRootObject rootObject:rootObject];
+ }
+
+ if (value.isString()) {
+ ExecState* exec = rootObject->globalObject()->globalExec();
+ const UString& u = asString(value)->value(exec);
+ return [NSString stringWithCharacters:u.characters() length:u.length()];
+ }
+
+ if (value.isNumber())
+ return [NSNumber numberWithDouble:value.uncheckedGetNumber()];
+
+ if (value.isBoolean())
+ return [NSNumber numberWithBool:value.getBoolean()];
+
+ if (value.isUndefined())
+ return [WebUndefined undefined];
+
+ // jsNull is not returned as NSNull because existing applications do not expect
+ // that return value. Return as nil for compatibility. <rdar://problem/4651318> <rdar://problem/4701626>
+ // Other types (e.g., UnspecifiedType) also return as nil.
+ return nil;
+}
+
+@end
+
+@interface WebScriptObject (WebKitCocoaBindings)
+
+- (id)objectAtIndex:(unsigned)index;
+
+@end
+
+@implementation WebScriptObject (WebKitCocoaBindings)
+
+#if 0
+
+// FIXME: We'd like to add this, but we can't do that until this issue is resolved:
+// http://bugs.webkit.org/show_bug.cgi?id=13129: presence of 'count' method on
+// WebScriptObject breaks Democracy player.
+
+- (unsigned)count
+{
+ id length = [self valueForKey:@"length"];
+ if (![length respondsToSelector:@selector(intValue)])
+ return 0;
+ return [length intValue];
+}
+
+#endif
+
+- (id)objectAtIndex:(unsigned)index
+{
+ return [self webScriptValueAtIndex:index];
+}
+
+@end
+
+@implementation WebUndefined
+
++ (id)allocWithZone:(NSZone *)unusedZone
+{
+ UNUSED_PARAM(unusedZone);
+
+ static WebUndefined *sharedUndefined = 0;
+ if (!sharedUndefined)
+ sharedUndefined = [super allocWithZone:NULL];
+ return sharedUndefined;
+}
+
+- (NSString *)description
+{
+ return @"undefined";
+}
+
+- (id)initWithCoder:(NSCoder *)unusedCoder
+{
+ UNUSED_PARAM(unusedCoder);
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)unusedCoder
+{
+ UNUSED_PARAM(unusedCoder);
+}
+
+- (id)copyWithZone:(NSZone *)unusedZone
+{
+ UNUSED_PARAM(unusedZone);
+
+ return self;
+}
+
+- (id)retain
+{
+ return self;
+}
+
+- (void)release
+{
+}
+
+- (NSUInteger)retainCount
+{
+ return UINT_MAX;
+}
+
+- (id)autorelease
+{
+ return self;
+}
+
+- (void)dealloc
+{
+ ASSERT(false);
+ return;
+ [super dealloc]; // make -Wdealloc-check happy
+}
+
++ (WebUndefined *)undefined
+{
+ return [WebUndefined allocWithZone:NULL];
+}
+
+@end