2 * Copyright (C) 2004, 2006, 2007, 2008 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"
31 #import "DOMInternal.h"
34 #import "JSDOMWindow.h"
35 #import "JSDOMWindowCustom.h"
36 #import "JSHTMLElement.h"
37 #import "JSMainThreadExecState.h"
38 #import "JSPluginElementFunctions.h"
39 #import "ObjCRuntimeObject.h"
40 #import "PlatformString.h"
41 #import "StringSourceProvider.h"
42 #import "WebCoreObjCExtras.h"
43 #import "objc_instance.h"
44 #import "runtime_object.h"
45 #import "runtime_root.h"
46 #import <JavaScriptCore/APICast.h>
47 #import <interpreter/CallFrame.h>
48 #import <runtime/InitializeThreading.h>
49 #import <runtime/JSGlobalObject.h>
50 #import <runtime/JSLock.h>
51 #import <runtime/Completion.h>
52 #import <runtime/Completion.h>
53 #import <wtf/Threading.h>
57 using namespace JSC::Bindings;
58 using namespace WebCore;
62 static NSMapTable* JSWrapperCache;
64 NSObject* getJSWrapper(JSObject* impl)
68 return static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
71 void addJSWrapper(NSObject* wrapper, JSObject* impl)
74 JSWrapperCache = createWrapperCache();
75 NSMapInsert(JSWrapperCache, impl, wrapper);
78 void removeJSWrapper(JSObject* impl)
82 NSMapRemove(JSWrapperCache, impl);
85 id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root)
87 if (id wrapper = getJSWrapper(object))
88 return [[wrapper retain] autorelease];
89 return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
92 static void addExceptionToConsole(ExecState* exec)
94 JSDOMWindow* window = asJSDOMWindow(exec->dynamicGlobalObject());
95 if (!window || !exec->hadException())
97 reportCurrentException(exec);
100 } // namespace WebCore
102 @implementation WebScriptObjectPrivate
106 @implementation WebScriptObject
110 JSC::initializeThreading();
111 WTF::initializeMainThreadToProcessMainThread();
112 WebCoreObjCFinalizeOnMainThread(self);
115 + (id)scriptObjectForJSObject:(JSObjectRef)jsObject originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
117 if (id domWrapper = createDOMWrapper(toJS(jsObject), originRootObject, rootObject))
120 return WebCore::createJSWrapper(toJS(jsObject), originRootObject, rootObject);
123 static void _didExecute(WebScriptObject *obj)
125 ASSERT(JSLock::lockCount() > 0);
127 RootObject* root = [obj _rootObject];
131 ExecState* exec = root->globalObject()->globalExec();
132 KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction();
134 func(exec, root->globalObject());
137 - (void)_setImp:(JSObject*)imp originRootObject:(PassRefPtr<RootObject>)originRootObject rootObject:(PassRefPtr<RootObject>)rootObject
139 // This function should only be called once, as a (possibly lazy) initializer.
140 ASSERT(!_private->imp);
141 ASSERT(!_private->rootObject);
142 ASSERT(!_private->originRootObject);
146 _private->rootObject = rootObject.releaseRef();
147 _private->originRootObject = originRootObject.releaseRef();
149 WebCore::addJSWrapper(self, imp);
151 if (_private->rootObject)
152 _private->rootObject->gcProtect(imp);
155 - (void)_setOriginRootObject:(PassRefPtr<RootObject>)originRootObject andRootObject:(PassRefPtr<RootObject>)rootObject
157 ASSERT(_private->imp);
160 rootObject->gcProtect(_private->imp);
162 if (_private->rootObject && _private->rootObject->isValid())
163 _private->rootObject->gcUnprotect(_private->imp);
165 if (_private->rootObject)
166 _private->rootObject->deref();
168 if (_private->originRootObject)
169 _private->originRootObject->deref();
171 _private->rootObject = rootObject.releaseRef();
172 _private->originRootObject = originRootObject.releaseRef();
175 - (id)_initWithJSObject:(JSC::JSObject*)imp originRootObject:(PassRefPtr<JSC::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<JSC::Bindings::RootObject>)rootObject
180 _private = [[WebScriptObjectPrivate alloc] init];
181 [self _setImp:imp originRootObject:originRootObject rootObject:rootObject];
188 // Associate the WebScriptObject with the JS wrapper for the ObjC DOM wrapper.
189 // This is done on lazily, on demand.
190 if (!_private->imp && _private->isCreatedByDOMWrapper)
191 [self _initializeScriptDOMNodeImp];
192 return [self _rootObject] ? _private->imp : 0;
197 return _private->imp != nil;
200 // Node that DOMNode overrides this method. So you should almost always
201 // use this method call instead of _private->rootObject directly.
202 - (RootObject*)_rootObject
204 return _private->rootObject && _private->rootObject->isValid() ? _private->rootObject : 0;
207 - (RootObject *)_originRootObject
209 return _private->originRootObject && _private->originRootObject->isValid() ? _private->originRootObject : 0;
212 - (BOOL)_isSafeScript
214 RootObject *root = [self _rootObject];
218 if (!_private->originRootObject)
221 if (!_private->originRootObject->isValid())
224 return root->globalObject()->allowsAccessFrom(_private->originRootObject->globalObject());
229 if (WebCoreObjCScheduleDeallocateOnMainThread([WebScriptObject class], self))
233 WebCore::removeJSWrapper(_private->imp);
235 if (_private->rootObject && _private->rootObject->isValid())
236 _private->rootObject->gcUnprotect(_private->imp);
238 if (_private->rootObject)
239 _private->rootObject->deref();
241 if (_private->originRootObject)
242 _private->originRootObject->deref();
251 if (_private->rootObject && _private->rootObject->isValid())
252 _private->rootObject->gcUnprotect(_private->imp);
254 if (_private->rootObject)
255 _private->rootObject->deref();
257 if (_private->originRootObject)
258 _private->originRootObject->deref();
263 + (BOOL)throwException:(NSString *)exceptionMessage
265 ObjcInstance::setGlobalException(exceptionMessage);
269 static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* rootObject, MarkedArgumentBuffer& aList)
271 int i, numObjects = array ? [array count] : 0;
273 for (i = 0; i < numObjects; i++) {
274 id anObject = [array objectAtIndex:i];
275 aList.append(convertObjcValueToValue(exec, &anObject, ObjcObjectType, rootObject));
279 - (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args
281 if (![self _isSafeScript])
284 JSLock lock(SilenceAssertionsOnly);
286 // Look up the function object.
287 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
288 ASSERT(!exec->hadException());
290 JSValue function = [self _imp]->get(exec, Identifier(exec, stringToUString(String(name))));
292 CallType callType = getCallData(function, callData);
293 if (callType == CallTypeNone)
296 MarkedArgumentBuffer argList;
297 getListFromNSArray(exec, args, [self _rootObject], argList);
299 if (![self _isSafeScript])
302 [self _rootObject]->globalObject()->globalData().timeoutChecker.start();
303 JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, [self _imp], argList);
304 [self _rootObject]->globalObject()->globalData().timeoutChecker.stop();
306 if (exec->hadException()) {
307 addExceptionToConsole(exec);
308 result = jsUndefined();
309 exec->clearException();
312 // Convert and return the result of the function call.
313 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
320 - (id)evaluateWebScript:(NSString *)script
322 if (![self _isSafeScript])
325 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
326 ASSERT(!exec->hadException());
328 JSLock lock(SilenceAssertionsOnly);
330 [self _rootObject]->globalObject()->globalData().timeoutChecker.start();
331 JSValue returnValue = JSMainThreadExecState::evaluate(exec, [self _rootObject]->globalObject()->globalScopeChain(), makeSource(String(script)), JSC::JSValue(), 0);
332 [self _rootObject]->globalObject()->globalData().timeoutChecker.stop();
334 id resultObj = [WebScriptObject _convertValueToObjcValue:returnValue originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
341 - (void)setValue:(id)value forKey:(NSString *)key
343 if (![self _isSafeScript])
346 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
347 ASSERT(!exec->hadException());
349 JSLock lock(SilenceAssertionsOnly);
351 PutPropertySlot slot;
352 [self _imp]->put(exec, Identifier(exec, stringToUString(String(key))), convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]), slot);
354 if (exec->hadException()) {
355 addExceptionToConsole(exec);
356 exec->clearException();
362 - (id)valueForKey:(NSString *)key
364 if (![self _isSafeScript])
367 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
368 ASSERT(!exec->hadException());
372 // Need to scope this lock to ensure that we release the lock before calling
373 // [super valueForKey:key] which might throw an exception and bypass the JSLock destructor,
374 // leaving the lock permanently held
375 JSLock lock(SilenceAssertionsOnly);
377 JSValue result = [self _imp]->get(exec, Identifier(exec, stringToUString(String(key))));
379 if (exec->hadException()) {
380 addExceptionToConsole(exec);
381 result = jsUndefined();
382 exec->clearException();
385 resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
388 if ([resultObj isKindOfClass:[WebUndefined class]])
389 resultObj = [super valueForKey:key]; // defaults to throwing an exception
391 JSLock lock(SilenceAssertionsOnly);
397 - (void)removeWebScriptKey:(NSString *)key
399 if (![self _isSafeScript])
402 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
403 ASSERT(!exec->hadException());
405 JSLock lock(SilenceAssertionsOnly);
406 [self _imp]->deleteProperty(exec, Identifier(exec, stringToUString(String(key))));
408 if (exec->hadException()) {
409 addExceptionToConsole(exec);
410 exec->clearException();
416 - (BOOL)hasWebScriptKey:(NSString *)key
418 if (![self _isSafeScript])
421 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
422 ASSERT(!exec->hadException());
424 JSLock lock(SilenceAssertionsOnly);
425 BOOL result = [self _imp]->hasProperty(exec, Identifier(exec, stringToUString(String(key))));
427 if (exec->hadException()) {
428 addExceptionToConsole(exec);
429 exec->clearException();
437 - (NSString *)stringRepresentation
439 if (![self _isSafeScript]) {
440 // This is a workaround for a gcc 3.3 internal compiler error.
444 JSLock lock(SilenceAssertionsOnly);
445 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
447 id result = convertValueToObjcValue(exec, [self _imp], ObjcObjectType).objectValue;
449 NSString *description = [result description];
456 - (id)webScriptValueAtIndex:(unsigned)index
458 if (![self _isSafeScript])
461 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
462 ASSERT(!exec->hadException());
464 JSLock lock(SilenceAssertionsOnly);
465 JSValue result = [self _imp]->get(exec, index);
467 if (exec->hadException()) {
468 addExceptionToConsole(exec);
469 result = jsUndefined();
470 exec->clearException();
473 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
480 - (void)setWebScriptValueAtIndex:(unsigned)index value:(id)value
482 if (![self _isSafeScript])
485 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
486 ASSERT(!exec->hadException());
488 JSLock lock(SilenceAssertionsOnly);
489 [self _imp]->put(exec, index, convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]));
491 if (exec->hadException()) {
492 addExceptionToConsole(exec);
493 exec->clearException();
499 - (void)setException:(NSString *)description
501 if (![self _rootObject])
503 ObjcInstance::setGlobalException(description, [self _rootObject]->globalObject());
506 - (JSObjectRef)JSObject
508 if (![self _isSafeScript])
511 return toRef([self _imp]);
514 + (id)_convertValueToObjcValue:(JSValue)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
516 if (value.isObject()) {
517 JSObject* object = asObject(value);
518 JSLock lock(SilenceAssertionsOnly);
520 if (object->inherits(&JSHTMLElement::s_info)) {
521 // Plugin elements cache the instance internally.
522 HTMLElement* el = static_cast<JSHTMLElement*>(object)->impl();
523 ObjcInstance* instance = static_cast<ObjcInstance*>(pluginInstance(el));
525 return instance->getObject();
526 } else if (object->inherits(&ObjCRuntimeObject::s_info)) {
527 ObjCRuntimeObject* runtimeObject = static_cast<ObjCRuntimeObject*>(object);
528 ObjcInstance* instance = runtimeObject->getInternalObjCInstance();
530 return instance->getObject();
534 return [WebScriptObject scriptObjectForJSObject:toRef(object) originRootObject:originRootObject rootObject:rootObject];
537 if (value.isString()) {
538 ExecState* exec = rootObject->globalObject()->globalExec();
539 const UString& u = asString(value)->value(exec);
540 return [NSString stringWithCharacters:u.characters() length:u.length()];
543 if (value.isNumber())
544 return [NSNumber numberWithDouble:value.uncheckedGetNumber()];
546 if (value.isBoolean())
547 return [NSNumber numberWithBool:value.getBoolean()];
549 if (value.isUndefined())
550 return [WebUndefined undefined];
552 // jsNull is not returned as NSNull because existing applications do not expect
553 // that return value. Return as nil for compatibility. <rdar://problem/4651318> <rdar://problem/4701626>
554 // Other types (e.g., UnspecifiedType) also return as nil.
560 @interface WebScriptObject (WebKitCocoaBindings)
562 - (id)objectAtIndex:(unsigned)index;
566 @implementation WebScriptObject (WebKitCocoaBindings)
570 // FIXME: We'd like to add this, but we can't do that until this issue is resolved:
571 // http://bugs.webkit.org/show_bug.cgi?id=13129: presence of 'count' method on
572 // WebScriptObject breaks Democracy player.
576 id length = [self valueForKey:@"length"];
577 if (![length respondsToSelector:@selector(intValue)])
579 return [length intValue];
584 - (id)objectAtIndex:(unsigned)index
586 return [self webScriptValueAtIndex:index];
591 @implementation WebUndefined
593 + (id)allocWithZone:(NSZone *)unusedZone
595 UNUSED_PARAM(unusedZone);
597 static WebUndefined *sharedUndefined = 0;
598 if (!sharedUndefined)
599 sharedUndefined = [super allocWithZone:NULL];
600 return sharedUndefined;
603 - (NSString *)description
608 - (id)initWithCoder:(NSCoder *)unusedCoder
610 UNUSED_PARAM(unusedCoder);
615 - (void)encodeWithCoder:(NSCoder *)unusedCoder
617 UNUSED_PARAM(unusedCoder);
620 - (id)copyWithZone:(NSZone *)unusedZone
622 UNUSED_PARAM(unusedZone);
632 - (oneway void)release
636 - (NSUInteger)retainCount
638 return NSUIntegerMax;
649 [super dealloc]; // make -Wdealloc-check happy
652 + (WebUndefined *)undefined
654 return [WebUndefined allocWithZone:NULL];