initial import
[vuplus_webkit] / Source / WebCore / bindings / objc / WebScriptObject.mm
1 /*
2  * Copyright (C) 2004, 2006, 2007, 2008 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  */
25
26 #import "config.h"
27 #import "WebScriptObjectPrivate.h"
28
29 #import "BridgeJSC.h"
30 #import "Console.h"
31 #import "DOMInternal.h"
32 #import "DOMWindow.h"
33 #import "Frame.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>
54
55
56 using namespace JSC;
57 using namespace JSC::Bindings;
58 using namespace WebCore;
59
60 namespace WebCore {
61
62 static NSMapTable* JSWrapperCache;
63
64 NSObject* getJSWrapper(JSObject* impl)
65 {
66     if (!JSWrapperCache)
67         return nil;
68     return static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
69 }
70
71 void addJSWrapper(NSObject* wrapper, JSObject* impl)
72 {
73     if (!JSWrapperCache)
74         JSWrapperCache = createWrapperCache();
75     NSMapInsert(JSWrapperCache, impl, wrapper);
76 }
77
78 void removeJSWrapper(JSObject* impl)
79 {
80     if (!JSWrapperCache)
81         return;
82     NSMapRemove(JSWrapperCache, impl);
83 }
84
85 id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root)
86 {
87     if (id wrapper = getJSWrapper(object))
88         return [[wrapper retain] autorelease];
89     return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
90 }
91
92 static void addExceptionToConsole(ExecState* exec)
93 {
94     JSDOMWindow* window = asJSDOMWindow(exec->dynamicGlobalObject());
95     if (!window || !exec->hadException())
96         return;
97     reportCurrentException(exec);
98 }
99
100 } // namespace WebCore
101
102 @implementation WebScriptObjectPrivate
103
104 @end
105
106 @implementation WebScriptObject
107
108 + (void)initialize
109 {
110     JSC::initializeThreading();
111     WTF::initializeMainThreadToProcessMainThread();
112     WebCoreObjCFinalizeOnMainThread(self);
113 }
114
115 + (id)scriptObjectForJSObject:(JSObjectRef)jsObject originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
116 {
117     if (id domWrapper = createDOMWrapper(toJS(jsObject), originRootObject, rootObject))
118         return domWrapper;
119     
120     return WebCore::createJSWrapper(toJS(jsObject), originRootObject, rootObject);
121 }
122
123 static void _didExecute(WebScriptObject *obj)
124 {
125     ASSERT(JSLock::lockCount() > 0);
126     
127     RootObject* root = [obj _rootObject];
128     if (!root)
129         return;
130
131     ExecState* exec = root->globalObject()->globalExec();
132     KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction();
133     if (func)
134         func(exec, root->globalObject());
135 }
136
137 - (void)_setImp:(JSObject*)imp originRootObject:(PassRefPtr<RootObject>)originRootObject rootObject:(PassRefPtr<RootObject>)rootObject
138 {
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);
143     ASSERT(imp);
144
145     _private->imp = imp;
146     _private->rootObject = rootObject.releaseRef();
147     _private->originRootObject = originRootObject.releaseRef();
148
149     WebCore::addJSWrapper(self, imp);
150
151     if (_private->rootObject)
152         _private->rootObject->gcProtect(imp);
153 }
154
155 - (void)_setOriginRootObject:(PassRefPtr<RootObject>)originRootObject andRootObject:(PassRefPtr<RootObject>)rootObject
156 {
157     ASSERT(_private->imp);
158
159     if (rootObject)
160         rootObject->gcProtect(_private->imp);
161
162     if (_private->rootObject && _private->rootObject->isValid())
163         _private->rootObject->gcUnprotect(_private->imp);
164
165     if (_private->rootObject)
166         _private->rootObject->deref();
167
168     if (_private->originRootObject)
169         _private->originRootObject->deref();
170
171     _private->rootObject = rootObject.releaseRef();
172     _private->originRootObject = originRootObject.releaseRef();
173 }
174
175 - (id)_initWithJSObject:(JSC::JSObject*)imp originRootObject:(PassRefPtr<JSC::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<JSC::Bindings::RootObject>)rootObject
176 {
177     ASSERT(imp);
178
179     self = [super init];
180     _private = [[WebScriptObjectPrivate alloc] init];
181     [self _setImp:imp originRootObject:originRootObject rootObject:rootObject];
182     
183     return self;
184 }
185
186 - (JSObject*)_imp
187 {
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;
193 }
194
195 - (BOOL)_hasImp
196 {
197     return _private->imp != nil;
198 }
199
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
203 {
204     return _private->rootObject && _private->rootObject->isValid() ? _private->rootObject : 0;
205 }
206
207 - (RootObject *)_originRootObject
208 {
209     return _private->originRootObject && _private->originRootObject->isValid() ? _private->originRootObject : 0;
210 }
211
212 - (BOOL)_isSafeScript
213 {
214     RootObject *root = [self _rootObject];
215     if (!root)
216         return false;
217
218     if (!_private->originRootObject)
219         return true;
220
221     if (!_private->originRootObject->isValid())
222         return false;
223
224     return root->globalObject()->allowsAccessFrom(_private->originRootObject->globalObject());
225 }
226
227 - (void)dealloc
228 {
229     if (WebCoreObjCScheduleDeallocateOnMainThread([WebScriptObject class], self))
230         return;
231
232     if (_private->imp)
233         WebCore::removeJSWrapper(_private->imp);
234
235     if (_private->rootObject && _private->rootObject->isValid())
236         _private->rootObject->gcUnprotect(_private->imp);
237
238     if (_private->rootObject)
239         _private->rootObject->deref();
240
241     if (_private->originRootObject)
242         _private->originRootObject->deref();
243
244     [_private release];
245
246     [super dealloc];
247 }
248
249 - (void)finalize
250 {
251     if (_private->rootObject && _private->rootObject->isValid())
252         _private->rootObject->gcUnprotect(_private->imp);
253
254     if (_private->rootObject)
255         _private->rootObject->deref();
256
257     if (_private->originRootObject)
258         _private->originRootObject->deref();
259
260     [super finalize];
261 }
262
263 + (BOOL)throwException:(NSString *)exceptionMessage
264 {
265     ObjcInstance::setGlobalException(exceptionMessage);
266     return YES;
267 }
268
269 static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* rootObject, MarkedArgumentBuffer& aList)
270 {
271     int i, numObjects = array ? [array count] : 0;
272     
273     for (i = 0; i < numObjects; i++) {
274         id anObject = [array objectAtIndex:i];
275         aList.append(convertObjcValueToValue(exec, &anObject, ObjcObjectType, rootObject));
276     }
277 }
278
279 - (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args
280 {
281     if (![self _isSafeScript])
282         return nil;
283
284     JSLock lock(SilenceAssertionsOnly);
285     
286     // Look up the function object.
287     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
288     ASSERT(!exec->hadException());
289
290     JSValue function = [self _imp]->get(exec, Identifier(exec, stringToUString(String(name))));
291     CallData callData;
292     CallType callType = getCallData(function, callData);
293     if (callType == CallTypeNone)
294         return nil;
295
296     MarkedArgumentBuffer argList;
297     getListFromNSArray(exec, args, [self _rootObject], argList);
298
299     if (![self _isSafeScript])
300         return nil;
301
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();
305
306     if (exec->hadException()) {
307         addExceptionToConsole(exec);
308         result = jsUndefined();
309         exec->clearException();
310     }
311
312     // Convert and return the result of the function call.
313     id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
314
315     _didExecute(self);
316         
317     return resultObj;
318 }
319
320 - (id)evaluateWebScript:(NSString *)script
321 {
322     if (![self _isSafeScript])
323         return nil;
324     
325     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
326     ASSERT(!exec->hadException());
327
328     JSLock lock(SilenceAssertionsOnly);
329     
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();
333
334     id resultObj = [WebScriptObject _convertValueToObjcValue:returnValue originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
335     
336     _didExecute(self);
337     
338     return resultObj;
339 }
340
341 - (void)setValue:(id)value forKey:(NSString *)key
342 {
343     if (![self _isSafeScript])
344         return;
345
346     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
347     ASSERT(!exec->hadException());
348
349     JSLock lock(SilenceAssertionsOnly);
350
351     PutPropertySlot slot;
352     [self _imp]->put(exec, Identifier(exec, stringToUString(String(key))), convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]), slot);
353
354     if (exec->hadException()) {
355         addExceptionToConsole(exec);
356         exec->clearException();
357     }
358
359     _didExecute(self);
360 }
361
362 - (id)valueForKey:(NSString *)key
363 {
364     if (![self _isSafeScript])
365         return nil;
366
367     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
368     ASSERT(!exec->hadException());
369
370     id resultObj;
371     {
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);
376         
377         JSValue result = [self _imp]->get(exec, Identifier(exec, stringToUString(String(key))));
378         
379         if (exec->hadException()) {
380             addExceptionToConsole(exec);
381             result = jsUndefined();
382             exec->clearException();
383         }
384
385         resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
386     }
387     
388     if ([resultObj isKindOfClass:[WebUndefined class]])
389         resultObj = [super valueForKey:key];    // defaults to throwing an exception
390
391     JSLock lock(SilenceAssertionsOnly);
392     _didExecute(self);
393     
394     return resultObj;
395 }
396
397 - (void)removeWebScriptKey:(NSString *)key
398 {
399     if (![self _isSafeScript])
400         return;
401
402     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
403     ASSERT(!exec->hadException());
404
405     JSLock lock(SilenceAssertionsOnly);
406     [self _imp]->deleteProperty(exec, Identifier(exec, stringToUString(String(key))));
407
408     if (exec->hadException()) {
409         addExceptionToConsole(exec);
410         exec->clearException();
411     }
412
413     _didExecute(self);
414 }
415
416 - (BOOL)hasWebScriptKey:(NSString *)key
417 {
418     if (![self _isSafeScript])
419         return NO;
420
421     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
422     ASSERT(!exec->hadException());
423
424     JSLock lock(SilenceAssertionsOnly);
425     BOOL result = [self _imp]->hasProperty(exec, Identifier(exec, stringToUString(String(key))));
426
427     if (exec->hadException()) {
428         addExceptionToConsole(exec);
429         exec->clearException();
430     }
431
432     _didExecute(self);
433
434     return result;
435 }
436
437 - (NSString *)stringRepresentation
438 {
439     if (![self _isSafeScript]) {
440         // This is a workaround for a gcc 3.3 internal compiler error.
441         return @"Undefined";
442     }
443
444     JSLock lock(SilenceAssertionsOnly);
445     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
446     
447     id result = convertValueToObjcValue(exec, [self _imp], ObjcObjectType).objectValue;
448
449     NSString *description = [result description];
450
451     _didExecute(self);
452
453     return description;
454 }
455
456 - (id)webScriptValueAtIndex:(unsigned)index
457 {
458     if (![self _isSafeScript])
459         return nil;
460
461     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
462     ASSERT(!exec->hadException());
463
464     JSLock lock(SilenceAssertionsOnly);
465     JSValue result = [self _imp]->get(exec, index);
466
467     if (exec->hadException()) {
468         addExceptionToConsole(exec);
469         result = jsUndefined();
470         exec->clearException();
471     }
472
473     id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
474
475     _didExecute(self);
476
477     return resultObj;
478 }
479
480 - (void)setWebScriptValueAtIndex:(unsigned)index value:(id)value
481 {
482     if (![self _isSafeScript])
483         return;
484
485     ExecState* exec = [self _rootObject]->globalObject()->globalExec();
486     ASSERT(!exec->hadException());
487
488     JSLock lock(SilenceAssertionsOnly);
489     [self _imp]->put(exec, index, convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]));
490
491     if (exec->hadException()) {
492         addExceptionToConsole(exec);
493         exec->clearException();
494     }
495
496     _didExecute(self);
497 }
498
499 - (void)setException:(NSString *)description
500 {
501     if (![self _rootObject])
502         return;
503     ObjcInstance::setGlobalException(description, [self _rootObject]->globalObject());
504 }
505
506 - (JSObjectRef)JSObject
507 {
508     if (![self _isSafeScript])
509         return NULL;
510
511     return toRef([self _imp]);
512 }
513
514 + (id)_convertValueToObjcValue:(JSValue)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
515 {
516     if (value.isObject()) {
517         JSObject* object = asObject(value);
518         JSLock lock(SilenceAssertionsOnly);
519
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));
524             if (instance)
525                 return instance->getObject();
526         } else if (object->inherits(&ObjCRuntimeObject::s_info)) {
527             ObjCRuntimeObject* runtimeObject = static_cast<ObjCRuntimeObject*>(object);
528             ObjcInstance* instance = runtimeObject->getInternalObjCInstance();
529             if (instance)
530                 return instance->getObject();
531             return nil;
532         }
533
534         return [WebScriptObject scriptObjectForJSObject:toRef(object) originRootObject:originRootObject rootObject:rootObject];
535     }
536
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()];
541     }
542
543     if (value.isNumber())
544         return [NSNumber numberWithDouble:value.uncheckedGetNumber()];
545
546     if (value.isBoolean())
547         return [NSNumber numberWithBool:value.getBoolean()];
548
549     if (value.isUndefined())
550         return [WebUndefined undefined];
551
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.
555     return nil;
556 }
557
558 @end
559
560 @interface WebScriptObject (WebKitCocoaBindings)
561
562 - (id)objectAtIndex:(unsigned)index;
563
564 @end
565
566 @implementation WebScriptObject (WebKitCocoaBindings)
567
568 #if 0 
569
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.
573
574 - (unsigned)count
575 {
576     id length = [self valueForKey:@"length"];
577     if (![length respondsToSelector:@selector(intValue)])
578         return 0;
579     return [length intValue];
580 }
581
582 #endif
583
584 - (id)objectAtIndex:(unsigned)index
585 {
586     return [self webScriptValueAtIndex:index];
587 }
588
589 @end
590
591 @implementation WebUndefined
592
593 + (id)allocWithZone:(NSZone *)unusedZone
594 {
595     UNUSED_PARAM(unusedZone);
596
597     static WebUndefined *sharedUndefined = 0;
598     if (!sharedUndefined)
599         sharedUndefined = [super allocWithZone:NULL];
600     return sharedUndefined;
601 }
602
603 - (NSString *)description
604 {
605     return @"undefined";
606 }
607
608 - (id)initWithCoder:(NSCoder *)unusedCoder
609 {
610     UNUSED_PARAM(unusedCoder);
611
612     return self;
613 }
614
615 - (void)encodeWithCoder:(NSCoder *)unusedCoder
616 {
617     UNUSED_PARAM(unusedCoder);
618 }
619
620 - (id)copyWithZone:(NSZone *)unusedZone
621 {
622     UNUSED_PARAM(unusedZone);
623
624     return self;
625 }
626
627 - (id)retain
628 {
629     return self;
630 }
631
632 - (oneway void)release
633 {
634 }
635
636 - (NSUInteger)retainCount
637 {
638     return NSUIntegerMax;
639 }
640
641 - (id)autorelease
642 {
643     return self;
644 }
645
646 - (void)dealloc
647 {
648     return;
649     [super dealloc]; // make -Wdealloc-check happy
650 }
651
652 + (WebUndefined *)undefined
653 {
654     return [WebUndefined allocWithZone:NULL];
655 }
656
657 @end