2 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "WebScriptObjectPrivate.h"
30 #import "DOMInternal.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"
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;
54 using namespace JSC::Bindings;
55 using namespace WebCore;
59 typedef HashMap<JSObject*, NSObject*> JSWrapperMap;
60 static JSWrapperMap* JSWrapperCache;
62 NSObject* getJSWrapper(JSObject* impl)
66 return JSWrapperCache->get(impl);
69 void addJSWrapper(NSObject* wrapper, JSObject* impl)
72 JSWrapperCache = new JSWrapperMap;
73 JSWrapperCache->set(impl, wrapper);
76 void removeJSWrapper(JSObject* impl)
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())
95 window->impl()->console()->reportCurrentException(exec);
98 } // namespace WebCore
100 @implementation WebScriptObjectPrivate
104 @implementation WebScriptObject
106 #ifndef BUILDING_ON_TIGER
109 WebCoreObjCFinalizeOnMainThread(self);
113 + (id)scriptObjectForJSObject:(JSObjectRef)jsObject originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
115 if (id domWrapper = WebCore::createDOMWrapper(toJS(jsObject), originRootObject, rootObject))
118 return WebCore::createJSWrapper(toJS(jsObject), originRootObject, rootObject);
121 static void _didExecute(WebScriptObject *obj)
123 ASSERT(JSLock::lockCount() > 0);
125 RootObject* root = [obj _rootObject];
129 ExecState* exec = root->globalObject()->globalExec();
130 KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction();
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);
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);
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
178 _private = [[WebScriptObjectPrivate alloc] init];
179 [self _setImp:imp originRootObject:originRootObject rootObject:rootObject];
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;
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];
216 if (!_private->originRootObject)
219 if (!_private->originRootObject->isValid())
222 return root->globalObject()->allowsAccessFrom(_private->originRootObject->globalObject());
227 if (WebCoreObjCScheduleDeallocateOnMainThread([WebScriptObject class], self))
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();
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();
264 + (BOOL)throwException:(NSString *)exceptionMessage
266 ObjcInstance::setGlobalException(exceptionMessage);
270 static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* rootObject, ArgList& aList)
272 int i, numObjects = array ? [array count] : 0;
274 for (i = 0; i < numObjects; i++) {
275 id anObject = [array objectAtIndex:i];
276 aList.append(convertObjcValueToValue(exec, &anObject, ObjcObjectType, rootObject));
280 - (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args
282 if (![self _isSafeScript])
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)));
293 CallType callType = function->getCallData(callData);
294 if (callType == CallTypeNone)
298 getListFromNSArray(exec, args, [self _rootObject], argList);
300 if (![self _isSafeScript])
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();
313 // Convert and return the result of the function call.
314 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
321 - (id)evaluateWebScript:(NSString *)script
323 if (![self _isSafeScript])
326 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
327 ASSERT(!exec->hadException());
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();
337 if (type == Normal) {
338 result = completion.value();
340 result = jsUndefined();
342 result = jsUndefined();
344 if (exec->hadException()) {
345 addExceptionToConsole(exec);
346 result = jsUndefined();
347 exec->clearException();
350 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
357 - (void)setValue:(id)value forKey:(NSString *)key
359 if (![self _isSafeScript])
362 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
363 ASSERT(!exec->hadException());
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();
378 - (id)valueForKey:(NSString *)key
380 if (![self _isSafeScript])
383 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
384 ASSERT(!exec->hadException());
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
393 JSValue* result = [self _imp]->get(exec, Identifier(exec, String(key)));
395 if (exec->hadException()) {
396 addExceptionToConsole(exec);
397 result = jsUndefined();
398 exec->clearException();
401 resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
404 if ([resultObj isKindOfClass:[WebUndefined class]])
405 resultObj = [super valueForKey:key]; // defaults to throwing an exception
413 - (void)removeWebScriptKey:(NSString *)key
415 if (![self _isSafeScript])
418 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
419 ASSERT(!exec->hadException());
422 [self _imp]->deleteProperty(exec, Identifier(exec, String(key)));
424 if (exec->hadException()) {
425 addExceptionToConsole(exec);
426 exec->clearException();
432 - (NSString *)stringRepresentation
434 if (![self _isSafeScript])
435 // This is a workaround for a gcc 3.3 internal compiler error.
439 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
441 id result = convertValueToObjcValue(exec, [self _imp], ObjcObjectType).objectValue;
443 NSString *description = [result description];
450 - (id)webScriptValueAtIndex:(unsigned)index
452 if (![self _isSafeScript])
455 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
456 ASSERT(!exec->hadException());
459 JSValue* result = [self _imp]->get(exec, index);
461 if (exec->hadException()) {
462 addExceptionToConsole(exec);
463 result = jsUndefined();
464 exec->clearException();
467 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
474 - (void)setWebScriptValueAtIndex:(unsigned)index value:(id)value
476 if (![self _isSafeScript])
479 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
480 ASSERT(!exec->hadException());
483 [self _imp]->put(exec, index, convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]));
485 if (exec->hadException()) {
486 addExceptionToConsole(exec);
487 exec->clearException();
493 - (void)setException:(NSString *)description
495 if (![self _rootObject])
497 ObjcInstance::setGlobalException(description, [self _rootObject]->globalObject());
500 - (JSObjectRef)JSObject
502 if (![self _isSafeScript])
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();
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);
521 if (object->classInfo() == &RuntimeObjectImp::s_info) {
522 RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(object);
523 ObjcInstance *instance = static_cast<ObjcInstance*>(imp->getInternalInstance());
525 return instance->getObject();
529 return [WebScriptObject scriptObjectForJSObject:toRef(object) originRootObject:originRootObject rootObject:rootObject];
532 if (value->isString()) {
533 const UString& u = asString(value)->value();
534 return [NSString stringWithCharacters:u.data() length:u.size()];
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.
554 @interface WebScriptObject (WebKitCocoaBindings)
556 - (id)objectAtIndex:(unsigned)index;
560 @implementation WebScriptObject (WebKitCocoaBindings)
563 // FIXME: presence of 'count' method on WebScriptObject breaks Democracy player
564 // http://bugs.webkit.org/show_bug.cgi?id=13129
568 id length = [self valueForKey:@"length"];
569 if ([length respondsToSelector:@selector(intValue)])
570 return [length intValue];
577 - (id)objectAtIndex:(unsigned)index
579 return [self webScriptValueAtIndex:index];
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
599 - (id)initWithCoder:(NSCoder *)coder
604 - (void)encodeWithCoder:(NSCoder *)encoder
608 - (id)copyWithZone:(NSZone *)zone
622 - (NSUInteger)retainCount
636 [super dealloc]; // make -Wdealloc-check happy
639 + (WebUndefined *)undefined
641 return [WebUndefined allocWithZone:NULL];