2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebCore / bindings / objc / WebScriptObject.mm
blob6262b97c4e8b945816e82961b94072b9cab6d06f
1 /*
2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
26 #import "config.h"
27 #import "WebScriptObjectPrivate.h"
29 #import "Console.h"
30 #import "DOMInternal.h"
31 #import "DOMWindow.h"
32 #import "Frame.h"
33 #import "JSDOMWindow.h"
34 #import "JSDOMWindowCustom.h"
35 #import "PlatformString.h"
36 #import "StringSourceProvider.h"
37 #import "WebCoreObjCExtras.h"
38 #import "objc_instance.h"
39 #import "runtime.h"
40 #import "runtime_object.h"
41 #import "runtime_root.h"
42 #import <JavaScriptCore/APICast.h>
43 #import <runtime/ExecState.h>
44 #import <runtime/JSGlobalObject.h>
45 #import <runtime/JSLock.h>
46 #import <kjs/completion.h>
47 #import <kjs/interpreter.h>
49 #ifdef BUILDING_ON_TIGER
50 typedef unsigned NSUInteger;
51 #endif
53 using namespace JSC;
54 using namespace JSC::Bindings;
55 using namespace WebCore;
57 namespace WebCore {
59 typedef HashMap<JSObject*, NSObject*> JSWrapperMap;
60 static JSWrapperMap* JSWrapperCache;
62 NSObject* getJSWrapper(JSObject* impl)
64     if (!JSWrapperCache)
65         return nil;
66     return JSWrapperCache->get(impl);
69 void addJSWrapper(NSObject* wrapper, JSObject* impl)
71     if (!JSWrapperCache)
72         JSWrapperCache = new JSWrapperMap;
73     JSWrapperCache->set(impl, wrapper);
76 void removeJSWrapper(JSObject* impl)
78     if (!JSWrapperCache)
79         return;
80     JSWrapperCache->remove(impl);
83 id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root)
85     if (id wrapper = getJSWrapper(object))
86         return [[wrapper retain] autorelease];
87     return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
90 static void addExceptionToConsole(ExecState* exec)
92     JSDOMWindow* window = asJSDOMWindow(exec->dynamicGlobalObject());
93     if (!window || !exec->hadException())
94         return;
95     window->impl()->console()->reportCurrentException(exec);
98 } // namespace WebCore
100 @implementation WebScriptObjectPrivate
102 @end
104 @implementation WebScriptObject
106 #ifndef BUILDING_ON_TIGER
107 + (void)initialize
109     WebCoreObjCFinalizeOnMainThread(self);
111 #endif
113 + (id)scriptObjectForJSObject:(JSObjectRef)jsObject originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
115     if (id domWrapper = WebCore::createDOMWrapper(toJS(jsObject), originRootObject, rootObject))
116         return domWrapper;
117     
118     return WebCore::createJSWrapper(toJS(jsObject), originRootObject, rootObject);
121 static void _didExecute(WebScriptObject *obj)
123     ASSERT(JSLock::lockCount() > 0);
124     
125     RootObject* root = [obj _rootObject];
126     if (!root)
127         return;
129     ExecState* exec = root->globalObject()->globalExec();
130     KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction();
131     if (func)
132         func(exec, root->globalObject());
135 - (void)_setImp:(JSObject*)imp originRootObject:(PassRefPtr<RootObject>)originRootObject rootObject:(PassRefPtr<RootObject>)rootObject
137     // This function should only be called once, as a (possibly lazy) initializer.
138     ASSERT(!_private->imp);
139     ASSERT(!_private->rootObject);
140     ASSERT(!_private->originRootObject);
141     ASSERT(imp);
143     _private->imp = imp;
144     _private->rootObject = rootObject.releaseRef();
145     _private->originRootObject = originRootObject.releaseRef();
147     WebCore::addJSWrapper(self, imp);
149     if (_private->rootObject)
150         _private->rootObject->gcProtect(imp);
153 - (void)_setOriginRootObject:(PassRefPtr<RootObject>)originRootObject andRootObject:(PassRefPtr<RootObject>)rootObject
155     ASSERT(_private->imp);
157     if (rootObject)
158         rootObject->gcProtect(_private->imp);
160     if (_private->rootObject && _private->rootObject->isValid())
161         _private->rootObject->gcUnprotect(_private->imp);
163     if (_private->rootObject)
164         _private->rootObject->deref();
166     if (_private->originRootObject)
167         _private->originRootObject->deref();
169     _private->rootObject = rootObject.releaseRef();
170     _private->originRootObject = originRootObject.releaseRef();
173 - (id)_initWithJSObject:(JSC::JSObject*)imp originRootObject:(PassRefPtr<JSC::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<JSC::Bindings::RootObject>)rootObject
175     ASSERT(imp);
177     self = [super init];
178     _private = [[WebScriptObjectPrivate alloc] init];
179     [self _setImp:imp originRootObject:originRootObject rootObject:rootObject];
180     
181     return self;
184 - (JSObject*)_imp
186     // Associate the WebScriptObject with the JS wrapper for the ObjC DOM wrapper.
187     // This is done on lazily, on demand.
188     if (!_private->imp && _private->isCreatedByDOMWrapper)
189         [self _initializeScriptDOMNodeImp];
190     return [self _rootObject] ? _private->imp : 0;
193 - (BOOL)_hasImp
195     return _private->imp != nil;
198 // Node that DOMNode overrides this method. So you should almost always
199 // use this method call instead of _private->rootObject directly.
200 - (RootObject*)_rootObject
202     return _private->rootObject && _private->rootObject->isValid() ? _private->rootObject : 0;
205 - (RootObject *)_originRootObject
207     return _private->originRootObject && _private->originRootObject->isValid() ? _private->originRootObject : 0;
210 - (BOOL)_isSafeScript
212     RootObject *root = [self _rootObject];
213     if (!root)
214         return false;
216     if (!_private->originRootObject)
217         return true;
219     if (!_private->originRootObject->isValid())
220         return false;
222     return root->globalObject()->allowsAccessFrom(_private->originRootObject->globalObject());
225 - (void)dealloc
227     if (WebCoreObjCScheduleDeallocateOnMainThread([WebScriptObject class], self))
228         return;
230     if (_private->imp)
231         WebCore::removeJSWrapper(_private->imp);
233     if (_private->rootObject && _private->rootObject->isValid())
234         _private->rootObject->gcUnprotect(_private->imp);
236     if (_private->rootObject)
237         _private->rootObject->deref();
239     if (_private->originRootObject)
240         _private->originRootObject->deref();
242     [_private release];
244     [super dealloc];
247 - (void)finalize
249     if (_private->imp)
250         WebCore::removeJSWrapper(_private->imp);
252     if (_private->rootObject && _private->rootObject->isValid())
253         _private->rootObject->gcUnprotect(_private->imp);
255     if (_private->rootObject)
256         _private->rootObject->deref();
258     if (_private->originRootObject)
259         _private->originRootObject->deref();
261     [super finalize];
264 + (BOOL)throwException:(NSString *)exceptionMessage
266     ObjcInstance::setGlobalException(exceptionMessage);
267     return YES;
270 static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* rootObject, ArgList& aList)
272     int i, numObjects = array ? [array count] : 0;
273     
274     for (i = 0; i < numObjects; i++) {
275         id anObject = [array objectAtIndex:i];
276         aList.append(convertObjcValueToValue(exec, &anObject, ObjcObjectType, rootObject));
277     }
280 - (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args
282     if (![self _isSafeScript])
283         return nil;
285     JSLock lock(false);
286     
287     // Look up the function object.
288     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
289     ASSERT(!exec->hadException());
291     JSValue* function = [self _imp]->get(exec, Identifier(exec, String(name)));
292     CallData callData;
293     CallType callType = function->getCallData(callData);
294     if (callType == CallTypeNone)
295         return nil;
297     ArgList argList;
298     getListFromNSArray(exec, args, [self _rootObject], argList);
300     if (![self _isSafeScript])
301         return nil;
303     [self _rootObject]->globalObject()->startTimeoutCheck();
304     JSValue* result = call(exec, function, callType, callData, [self _imp], argList);
305     [self _rootObject]->globalObject()->stopTimeoutCheck();
307     if (exec->hadException()) {
308         addExceptionToConsole(exec);
309         result = jsUndefined();
310         exec->clearException();
311     }
313     // Convert and return the result of the function call.
314     id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
316     _didExecute(self);
317         
318     return resultObj;
321 - (id)evaluateWebScript:(NSString *)script
323     if (![self _isSafeScript])
324         return nil;
325     
326     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
327     ASSERT(!exec->hadException());
329     JSValue* result;
330     JSLock lock(false);
331     
332     [self _rootObject]->globalObject()->startTimeoutCheck();
333     Completion completion = Interpreter::evaluate([self _rootObject]->globalObject()->globalExec(), [self _rootObject]->globalObject()->globalScopeChain(), makeSource(String(script)));
334     [self _rootObject]->globalObject()->stopTimeoutCheck();
335     ComplType type = completion.complType();
336     
337     if (type == Normal) {
338         result = completion.value();
339         if (!result)
340             result = jsUndefined();
341     } else
342         result = jsUndefined();
343     
344     if (exec->hadException()) {
345         addExceptionToConsole(exec);
346         result = jsUndefined();
347         exec->clearException();
348     }
349     
350     id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
351     
352     _didExecute(self);
353     
354     return resultObj;
357 - (void)setValue:(id)value forKey:(NSString *)key
359     if (![self _isSafeScript])
360         return;
362     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
363     ASSERT(!exec->hadException());
365     JSLock lock(false);
367     PutPropertySlot slot;
368     [self _imp]->put(exec, Identifier(exec, String(key)), convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]), slot);
370     if (exec->hadException()) {
371         addExceptionToConsole(exec);
372         exec->clearException();
373     }
375     _didExecute(self);
378 - (id)valueForKey:(NSString *)key
380     if (![self _isSafeScript])
381         return nil;
383     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
384     ASSERT(!exec->hadException());
386     id resultObj;
387     {
388         // Need to scope this lock to ensure that we release the lock before calling
389         // [super valueForKey:key] which might throw an exception and bypass the JSLock destructor,
390         // leaving the lock permanently held
391         JSLock lock(false);
392         
393         JSValue* result = [self _imp]->get(exec, Identifier(exec, String(key)));
394         
395         if (exec->hadException()) {
396             addExceptionToConsole(exec);
397             result = jsUndefined();
398             exec->clearException();
399         }
401         resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
402     }
403     
404     if ([resultObj isKindOfClass:[WebUndefined class]])
405         resultObj = [super valueForKey:key];    // defaults to throwing an exception
407     JSLock lock(false);
408     _didExecute(self);
409     
410     return resultObj;
413 - (void)removeWebScriptKey:(NSString *)key
415     if (![self _isSafeScript])
416         return;
418     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
419     ASSERT(!exec->hadException());
421     JSLock lock(false);
422     [self _imp]->deleteProperty(exec, Identifier(exec, String(key)));
424     if (exec->hadException()) {
425         addExceptionToConsole(exec);
426         exec->clearException();
427     }
429     _didExecute(self);
432 - (NSString *)stringRepresentation
434     if (![self _isSafeScript])
435         // This is a workaround for a gcc 3.3 internal compiler error.
436         return @"Undefined";
438     JSLock lock(false);
439     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
440     
441     id result = convertValueToObjcValue(exec, [self _imp], ObjcObjectType).objectValue;
443     NSString *description = [result description];
445     _didExecute(self);
447     return description;
450 - (id)webScriptValueAtIndex:(unsigned)index
452     if (![self _isSafeScript])
453         return nil;
455     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
456     ASSERT(!exec->hadException());
458     JSLock lock(false);
459     JSValue* result = [self _imp]->get(exec, index);
461     if (exec->hadException()) {
462         addExceptionToConsole(exec);
463         result = jsUndefined();
464         exec->clearException();
465     }
467     id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
469     _didExecute(self);
471     return resultObj;
474 - (void)setWebScriptValueAtIndex:(unsigned)index value:(id)value
476     if (![self _isSafeScript])
477         return;
479     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
480     ASSERT(!exec->hadException());
482     JSLock lock(false);
483     [self _imp]->put(exec, index, convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]));
485     if (exec->hadException()) {
486         addExceptionToConsole(exec);
487         exec->clearException();
488     }
490     _didExecute(self);
493 - (void)setException:(NSString *)description
495     if (![self _rootObject])
496         return;
497     ObjcInstance::setGlobalException(description, [self _rootObject]->globalObject());
500 - (JSObjectRef)JSObject
502     if (![self _isSafeScript])
503         return NULL;
505     return toRef([self _imp]);
508 + (id)_convertValueToObjcValue:(JSValue*)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
510     if (value->isObject()) {
511         JSObject* object = asObject(value);
512         ExecState* exec = rootObject->globalObject()->globalExec();
513         JSLock lock(false);
514         
515         if (object->classInfo() != &RuntimeObjectImp::s_info) {
516             JSValue* runtimeObject = object->get(exec, Identifier(exec, "__apple_runtime_object"));
517             if (runtimeObject && runtimeObject->isObject())
518                 object = asObject(runtimeObject);
519         }
521         if (object->classInfo() == &RuntimeObjectImp::s_info) {
522             RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(object);
523             ObjcInstance *instance = static_cast<ObjcInstance*>(imp->getInternalInstance());
524             if (instance)
525                 return instance->getObject();
526             return nil;
527         }
529         return [WebScriptObject scriptObjectForJSObject:toRef(object) originRootObject:originRootObject rootObject:rootObject];
530     }
532     if (value->isString()) {
533         const UString& u = asString(value)->value();
534         return [NSString stringWithCharacters:u.data() length:u.size()];
535     }
537     if (value->isNumber())
538         return [NSNumber numberWithDouble:value->getNumber()];
540     if (value->isBoolean())
541         return [NSNumber numberWithBool:value->getBoolean()];
543     if (value->isUndefined())
544         return [WebUndefined undefined];
546     // jsNull is not returned as NSNull because existing applications do not expect
547     // that return value. Return as nil for compatibility. <rdar://problem/4651318> <rdar://problem/4701626>
548     // Other types (e.g., UnspecifiedType) also return as nil.
549     return nil;
552 @end
554 @interface WebScriptObject (WebKitCocoaBindings)
556 - (id)objectAtIndex:(unsigned)index;
558 @end
560 @implementation WebScriptObject (WebKitCocoaBindings)
562 #if 0 
563 // FIXME: presence of 'count' method on WebScriptObject breaks Democracy player
564 //        http://bugs.webkit.org/show_bug.cgi?id=13129
566 - (unsigned)count
568     id length = [self valueForKey:@"length"];
569     if ([length respondsToSelector:@selector(intValue)])
570         return [length intValue];
571     else
572         return 0;
575 #endif
577 - (id)objectAtIndex:(unsigned)index
579     return [self webScriptValueAtIndex:index];
582 @end
584 @implementation WebUndefined
586 + (id)allocWithZone:(NSZone *)zone
588     static WebUndefined *sharedUndefined = 0;
589     if (!sharedUndefined)
590         sharedUndefined = [super allocWithZone:NULL];
591     return sharedUndefined;
594 - (NSString *)description
596     return @"undefined";
599 - (id)initWithCoder:(NSCoder *)coder
601     return self;
604 - (void)encodeWithCoder:(NSCoder *)encoder
608 - (id)copyWithZone:(NSZone *)zone
610     return self;
613 - (id)retain
615     return self;
618 - (void)release
622 - (NSUInteger)retainCount
624     return UINT_MAX;
627 - (id)autorelease
629     return self;
632 - (void)dealloc
634     ASSERT(false);
635     return;
636     [super dealloc]; // make -Wdealloc-check happy
639 + (WebUndefined *)undefined
641     return [WebUndefined allocWithZone:NULL];
644 @end