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>
55 #ifdef BUILDING_ON_TIGER
56 typedef unsigned NSUInteger;
60 using namespace JSC::Bindings;
61 using namespace WebCore;
65 static NSMapTable* JSWrapperCache;
67 NSObject* getJSWrapper(JSObject* impl)
71 return static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
74 void addJSWrapper(NSObject* wrapper, JSObject* impl)
77 JSWrapperCache = createWrapperCache();
78 NSMapInsert(JSWrapperCache, impl, wrapper);
81 void removeJSWrapper(JSObject* impl)
85 NSMapRemove(JSWrapperCache, impl);
88 id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root)
90 if (id wrapper = getJSWrapper(object))
91 return [[wrapper retain] autorelease];
92 return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
95 static void addExceptionToConsole(ExecState* exec)
97 JSDOMWindow* window = asJSDOMWindow(exec->dynamicGlobalObject());
98 if (!window || !exec->hadException())
100 reportCurrentException(exec);
103 } // namespace WebCore
105 @implementation WebScriptObjectPrivate
109 @implementation WebScriptObject
113 JSC::initializeThreading();
114 WTF::initializeMainThreadToProcessMainThread();
115 #ifndef BUILDING_ON_TIGER
116 WebCoreObjCFinalizeOnMainThread(self);
120 + (id)scriptObjectForJSObject:(JSObjectRef)jsObject originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
122 if (id domWrapper = createDOMWrapper(toJS(jsObject), originRootObject, rootObject))
125 return WebCore::createJSWrapper(toJS(jsObject), originRootObject, rootObject);
128 static void _didExecute(WebScriptObject *obj)
130 ASSERT(JSLock::lockCount() > 0);
132 RootObject* root = [obj _rootObject];
136 ExecState* exec = root->globalObject()->globalExec();
137 KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction();
139 func(exec, root->globalObject());
142 - (void)_setImp:(JSObject*)imp originRootObject:(PassRefPtr<RootObject>)originRootObject rootObject:(PassRefPtr<RootObject>)rootObject
144 // This function should only be called once, as a (possibly lazy) initializer.
145 ASSERT(!_private->imp);
146 ASSERT(!_private->rootObject);
147 ASSERT(!_private->originRootObject);
151 _private->rootObject = rootObject.releaseRef();
152 _private->originRootObject = originRootObject.releaseRef();
154 WebCore::addJSWrapper(self, imp);
156 if (_private->rootObject)
157 _private->rootObject->gcProtect(imp);
160 - (void)_setOriginRootObject:(PassRefPtr<RootObject>)originRootObject andRootObject:(PassRefPtr<RootObject>)rootObject
162 ASSERT(_private->imp);
165 rootObject->gcProtect(_private->imp);
167 if (_private->rootObject && _private->rootObject->isValid())
168 _private->rootObject->gcUnprotect(_private->imp);
170 if (_private->rootObject)
171 _private->rootObject->deref();
173 if (_private->originRootObject)
174 _private->originRootObject->deref();
176 _private->rootObject = rootObject.releaseRef();
177 _private->originRootObject = originRootObject.releaseRef();
180 - (id)_initWithJSObject:(JSC::JSObject*)imp originRootObject:(PassRefPtr<JSC::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<JSC::Bindings::RootObject>)rootObject
185 _private = [[WebScriptObjectPrivate alloc] init];
186 [self _setImp:imp originRootObject:originRootObject rootObject:rootObject];
193 // Associate the WebScriptObject with the JS wrapper for the ObjC DOM wrapper.
194 // This is done on lazily, on demand.
195 if (!_private->imp && _private->isCreatedByDOMWrapper)
196 [self _initializeScriptDOMNodeImp];
197 return [self _rootObject] ? _private->imp : 0;
202 return _private->imp != nil;
205 // Node that DOMNode overrides this method. So you should almost always
206 // use this method call instead of _private->rootObject directly.
207 - (RootObject*)_rootObject
209 return _private->rootObject && _private->rootObject->isValid() ? _private->rootObject : 0;
212 - (RootObject *)_originRootObject
214 return _private->originRootObject && _private->originRootObject->isValid() ? _private->originRootObject : 0;
217 - (BOOL)_isSafeScript
219 RootObject *root = [self _rootObject];
223 if (!_private->originRootObject)
226 if (!_private->originRootObject->isValid())
229 return root->globalObject()->allowsAccessFrom(_private->originRootObject->globalObject());
234 if (WebCoreObjCScheduleDeallocateOnMainThread([WebScriptObject class], self))
238 WebCore::removeJSWrapper(_private->imp);
240 if (_private->rootObject && _private->rootObject->isValid())
241 _private->rootObject->gcUnprotect(_private->imp);
243 if (_private->rootObject)
244 _private->rootObject->deref();
246 if (_private->originRootObject)
247 _private->originRootObject->deref();
256 if (_private->rootObject && _private->rootObject->isValid())
257 _private->rootObject->gcUnprotect(_private->imp);
259 if (_private->rootObject)
260 _private->rootObject->deref();
262 if (_private->originRootObject)
263 _private->originRootObject->deref();
268 + (BOOL)throwException:(NSString *)exceptionMessage
270 ObjcInstance::setGlobalException(exceptionMessage);
274 static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* rootObject, MarkedArgumentBuffer& aList)
276 int i, numObjects = array ? [array count] : 0;
278 for (i = 0; i < numObjects; i++) {
279 id anObject = [array objectAtIndex:i];
280 aList.append(convertObjcValueToValue(exec, &anObject, ObjcObjectType, rootObject));
284 - (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args
286 if (![self _isSafeScript])
289 JSLock lock(SilenceAssertionsOnly);
291 // Look up the function object.
292 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
293 ASSERT(!exec->hadException());
295 JSValue function = [self _imp]->get(exec, Identifier(exec, stringToUString(String(name))));
297 CallType callType = getCallData(function, callData);
298 if (callType == CallTypeNone)
301 MarkedArgumentBuffer argList;
302 getListFromNSArray(exec, args, [self _rootObject], argList);
304 if (![self _isSafeScript])
307 [self _rootObject]->globalObject()->globalData().timeoutChecker.start();
308 JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, [self _imp], argList);
309 [self _rootObject]->globalObject()->globalData().timeoutChecker.stop();
311 if (exec->hadException()) {
312 addExceptionToConsole(exec);
313 result = jsUndefined();
314 exec->clearException();
317 // Convert and return the result of the function call.
318 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
325 - (id)evaluateWebScript:(NSString *)script
327 if (![self _isSafeScript])
330 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
331 ASSERT(!exec->hadException());
334 JSLock lock(SilenceAssertionsOnly);
336 [self _rootObject]->globalObject()->globalData().timeoutChecker.start();
337 Completion completion = JSMainThreadExecState::evaluate([self _rootObject]->globalObject()->globalExec(), [self _rootObject]->globalObject()->globalScopeChain(), makeSource(String(script)), JSC::JSValue());
338 [self _rootObject]->globalObject()->globalData().timeoutChecker.stop();
339 ComplType type = completion.complType();
341 if (type == Normal) {
342 result = completion.value();
344 result = jsUndefined();
346 result = jsUndefined();
348 if (exec->hadException()) {
349 addExceptionToConsole(exec);
350 result = jsUndefined();
351 exec->clearException();
354 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
361 - (void)setValue:(id)value forKey:(NSString *)key
363 if (![self _isSafeScript])
366 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
367 ASSERT(!exec->hadException());
369 JSLock lock(SilenceAssertionsOnly);
371 PutPropertySlot slot;
372 [self _imp]->put(exec, Identifier(exec, stringToUString(String(key))), convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]), slot);
374 if (exec->hadException()) {
375 addExceptionToConsole(exec);
376 exec->clearException();
382 - (id)valueForKey:(NSString *)key
384 if (![self _isSafeScript])
387 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
388 ASSERT(!exec->hadException());
392 // Need to scope this lock to ensure that we release the lock before calling
393 // [super valueForKey:key] which might throw an exception and bypass the JSLock destructor,
394 // leaving the lock permanently held
395 JSLock lock(SilenceAssertionsOnly);
397 JSValue result = [self _imp]->get(exec, Identifier(exec, stringToUString(String(key))));
399 if (exec->hadException()) {
400 addExceptionToConsole(exec);
401 result = jsUndefined();
402 exec->clearException();
405 resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
408 if ([resultObj isKindOfClass:[WebUndefined class]])
409 resultObj = [super valueForKey:key]; // defaults to throwing an exception
411 JSLock lock(SilenceAssertionsOnly);
417 - (void)removeWebScriptKey:(NSString *)key
419 if (![self _isSafeScript])
422 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
423 ASSERT(!exec->hadException());
425 JSLock lock(SilenceAssertionsOnly);
426 [self _imp]->deleteProperty(exec, Identifier(exec, stringToUString(String(key))));
428 if (exec->hadException()) {
429 addExceptionToConsole(exec);
430 exec->clearException();
436 - (BOOL)hasWebScriptKey:(NSString *)key
438 if (![self _isSafeScript])
441 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
442 ASSERT(!exec->hadException());
444 JSLock lock(SilenceAssertionsOnly);
445 BOOL result = [self _imp]->hasProperty(exec, Identifier(exec, stringToUString(String(key))));
447 if (exec->hadException()) {
448 addExceptionToConsole(exec);
449 exec->clearException();
457 - (NSString *)stringRepresentation
459 if (![self _isSafeScript]) {
460 // This is a workaround for a gcc 3.3 internal compiler error.
464 JSLock lock(SilenceAssertionsOnly);
465 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
467 id result = convertValueToObjcValue(exec, [self _imp], ObjcObjectType).objectValue;
469 NSString *description = [result description];
476 - (id)webScriptValueAtIndex:(unsigned)index
478 if (![self _isSafeScript])
481 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
482 ASSERT(!exec->hadException());
484 JSLock lock(SilenceAssertionsOnly);
485 JSValue result = [self _imp]->get(exec, index);
487 if (exec->hadException()) {
488 addExceptionToConsole(exec);
489 result = jsUndefined();
490 exec->clearException();
493 id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
500 - (void)setWebScriptValueAtIndex:(unsigned)index value:(id)value
502 if (![self _isSafeScript])
505 ExecState* exec = [self _rootObject]->globalObject()->globalExec();
506 ASSERT(!exec->hadException());
508 JSLock lock(SilenceAssertionsOnly);
509 [self _imp]->put(exec, index, convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]));
511 if (exec->hadException()) {
512 addExceptionToConsole(exec);
513 exec->clearException();
519 - (void)setException:(NSString *)description
521 if (![self _rootObject])
523 ObjcInstance::setGlobalException(description, [self _rootObject]->globalObject());
526 - (JSObjectRef)JSObject
528 if (![self _isSafeScript])
531 return toRef([self _imp]);
534 + (id)_convertValueToObjcValue:(JSValue)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
536 if (value.isObject()) {
537 JSObject* object = asObject(value);
538 JSLock lock(SilenceAssertionsOnly);
540 if (object->inherits(&JSHTMLElement::s_info)) {
541 // Plugin elements cache the instance internally.
542 HTMLElement* el = static_cast<JSHTMLElement*>(object)->impl();
543 ObjcInstance* instance = static_cast<ObjcInstance*>(pluginInstance(el));
545 return instance->getObject();
546 } else if (object->inherits(&ObjCRuntimeObject::s_info)) {
547 ObjCRuntimeObject* runtimeObject = static_cast<ObjCRuntimeObject*>(object);
548 ObjcInstance* instance = runtimeObject->getInternalObjCInstance();
550 return instance->getObject();
554 return [WebScriptObject scriptObjectForJSObject:toRef(object) originRootObject:originRootObject rootObject:rootObject];
557 if (value.isString()) {
558 ExecState* exec = rootObject->globalObject()->globalExec();
559 const UString& u = asString(value)->value(exec);
560 return [NSString stringWithCharacters:u.characters() length:u.length()];
563 if (value.isNumber())
564 return [NSNumber numberWithDouble:value.uncheckedGetNumber()];
566 if (value.isBoolean())
567 return [NSNumber numberWithBool:value.getBoolean()];
569 if (value.isUndefined())
570 return [WebUndefined undefined];
572 // jsNull is not returned as NSNull because existing applications do not expect
573 // that return value. Return as nil for compatibility. <rdar://problem/4651318> <rdar://problem/4701626>
574 // Other types (e.g., UnspecifiedType) also return as nil.
580 @interface WebScriptObject (WebKitCocoaBindings)
582 - (id)objectAtIndex:(unsigned)index;
586 @implementation WebScriptObject (WebKitCocoaBindings)
590 // FIXME: We'd like to add this, but we can't do that until this issue is resolved:
591 // http://bugs.webkit.org/show_bug.cgi?id=13129: presence of 'count' method on
592 // WebScriptObject breaks Democracy player.
596 id length = [self valueForKey:@"length"];
597 if (![length respondsToSelector:@selector(intValue)])
599 return [length intValue];
604 - (id)objectAtIndex:(unsigned)index
606 return [self webScriptValueAtIndex:index];
611 @implementation WebUndefined
613 + (id)allocWithZone:(NSZone *)unusedZone
615 UNUSED_PARAM(unusedZone);
617 static WebUndefined *sharedUndefined = 0;
618 if (!sharedUndefined)
619 sharedUndefined = [super allocWithZone:NULL];
620 return sharedUndefined;
623 - (NSString *)description
628 - (id)initWithCoder:(NSCoder *)unusedCoder
630 UNUSED_PARAM(unusedCoder);
635 - (void)encodeWithCoder:(NSCoder *)unusedCoder
637 UNUSED_PARAM(unusedCoder);
640 - (id)copyWithZone:(NSZone *)unusedZone
642 UNUSED_PARAM(unusedZone);
656 - (NSUInteger)retainCount
670 [super dealloc]; // make -Wdealloc-check happy
673 + (WebUndefined *)undefined
675 return [WebUndefined allocWithZone:NULL];