• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 <interpreter/CallFrame.h>
44#import <runtime/InitializeThreading.h>
45#import <runtime/JSGlobalObject.h>
46#import <runtime/JSLock.h>
47#import <runtime/Completion.h>
48#import <runtime/Completion.h>
49
50#ifdef BUILDING_ON_TIGER
51typedef unsigned NSUInteger;
52#endif
53
54using namespace JSC;
55using namespace JSC::Bindings;
56using namespace WebCore;
57
58namespace WebCore {
59
60static NSMapTable* JSWrapperCache;
61
62NSObject* getJSWrapper(JSObject* impl)
63{
64    if (!JSWrapperCache)
65        return nil;
66    return static_cast<NSObject*>(NSMapGet(JSWrapperCache, impl));
67}
68
69void addJSWrapper(NSObject* wrapper, JSObject* impl)
70{
71    if (!JSWrapperCache)
72        JSWrapperCache = createWrapperCache();
73    NSMapInsert(JSWrapperCache, impl, wrapper);
74}
75
76void removeJSWrapper(JSObject* impl)
77{
78    if (!JSWrapperCache)
79        return;
80    NSMapRemove(JSWrapperCache, impl);
81}
82
83id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject> origin, PassRefPtr<JSC::Bindings::RootObject> root)
84{
85    if (id wrapper = getJSWrapper(object))
86        return [[wrapper retain] autorelease];
87    return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
88}
89
90static void addExceptionToConsole(ExecState* exec)
91{
92    JSDOMWindow* window = asJSDOMWindow(exec->dynamicGlobalObject());
93    if (!window || !exec->hadException())
94        return;
95    reportCurrentException(exec);
96}
97
98} // namespace WebCore
99
100@implementation WebScriptObjectPrivate
101
102@end
103
104@implementation WebScriptObject
105
106+ (void)initialize
107{
108    JSC::initializeThreading();
109#ifndef BUILDING_ON_TIGER
110    WebCoreObjCFinalizeOnMainThread(self);
111#endif
112}
113
114+ (id)scriptObjectForJSObject:(JSObjectRef)jsObject originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
115{
116    if (id domWrapper = createDOMWrapper(toJS(jsObject), originRootObject, rootObject))
117        return domWrapper;
118
119    return WebCore::createJSWrapper(toJS(jsObject), originRootObject, rootObject);
120}
121
122static void _didExecute(WebScriptObject *obj)
123{
124    ASSERT(JSLock::lockCount() > 0);
125
126    RootObject* root = [obj _rootObject];
127    if (!root)
128        return;
129
130    ExecState* exec = root->globalObject()->globalExec();
131    KJSDidExecuteFunctionPtr func = Instance::didExecuteFunction();
132    if (func)
133        func(exec, root->globalObject());
134}
135
136- (void)_setImp:(JSObject*)imp originRootObject:(PassRefPtr<RootObject>)originRootObject rootObject:(PassRefPtr<RootObject>)rootObject
137{
138    // This function should only be called once, as a (possibly lazy) initializer.
139    ASSERT(!_private->imp);
140    ASSERT(!_private->rootObject);
141    ASSERT(!_private->originRootObject);
142    ASSERT(imp);
143
144    _private->imp = imp;
145    _private->rootObject = rootObject.releaseRef();
146    _private->originRootObject = originRootObject.releaseRef();
147
148    WebCore::addJSWrapper(self, imp);
149
150    if (_private->rootObject)
151        _private->rootObject->gcProtect(imp);
152}
153
154- (void)_setOriginRootObject:(PassRefPtr<RootObject>)originRootObject andRootObject:(PassRefPtr<RootObject>)rootObject
155{
156    ASSERT(_private->imp);
157
158    if (rootObject)
159        rootObject->gcProtect(_private->imp);
160
161    if (_private->rootObject && _private->rootObject->isValid())
162        _private->rootObject->gcUnprotect(_private->imp);
163
164    if (_private->rootObject)
165        _private->rootObject->deref();
166
167    if (_private->originRootObject)
168        _private->originRootObject->deref();
169
170    _private->rootObject = rootObject.releaseRef();
171    _private->originRootObject = originRootObject.releaseRef();
172}
173
174- (id)_initWithJSObject:(JSC::JSObject*)imp originRootObject:(PassRefPtr<JSC::Bindings::RootObject>)originRootObject rootObject:(PassRefPtr<JSC::Bindings::RootObject>)rootObject
175{
176    ASSERT(imp);
177
178    self = [super init];
179    _private = [[WebScriptObjectPrivate alloc] init];
180    [self _setImp:imp originRootObject:originRootObject rootObject:rootObject];
181
182    return self;
183}
184
185- (JSObject*)_imp
186{
187    // Associate the WebScriptObject with the JS wrapper for the ObjC DOM wrapper.
188    // This is done on lazily, on demand.
189    if (!_private->imp && _private->isCreatedByDOMWrapper)
190        [self _initializeScriptDOMNodeImp];
191    return [self _rootObject] ? _private->imp : 0;
192}
193
194- (BOOL)_hasImp
195{
196    return _private->imp != nil;
197}
198
199// Node that DOMNode overrides this method. So you should almost always
200// use this method call instead of _private->rootObject directly.
201- (RootObject*)_rootObject
202{
203    return _private->rootObject && _private->rootObject->isValid() ? _private->rootObject : 0;
204}
205
206- (RootObject *)_originRootObject
207{
208    return _private->originRootObject && _private->originRootObject->isValid() ? _private->originRootObject : 0;
209}
210
211- (BOOL)_isSafeScript
212{
213    RootObject *root = [self _rootObject];
214    if (!root)
215        return false;
216
217    if (!_private->originRootObject)
218        return true;
219
220    if (!_private->originRootObject->isValid())
221        return false;
222
223    return root->globalObject()->allowsAccessFrom(_private->originRootObject->globalObject());
224}
225
226- (void)dealloc
227{
228    if (WebCoreObjCScheduleDeallocateOnMainThread([WebScriptObject class], self))
229        return;
230
231    if (_private->imp)
232        WebCore::removeJSWrapper(_private->imp);
233
234    if (_private->rootObject && _private->rootObject->isValid())
235        _private->rootObject->gcUnprotect(_private->imp);
236
237    if (_private->rootObject)
238        _private->rootObject->deref();
239
240    if (_private->originRootObject)
241        _private->originRootObject->deref();
242
243    [_private release];
244
245    [super dealloc];
246}
247
248- (void)finalize
249{
250    if (_private->rootObject && _private->rootObject->isValid())
251        _private->rootObject->gcUnprotect(_private->imp);
252
253    if (_private->rootObject)
254        _private->rootObject->deref();
255
256    if (_private->originRootObject)
257        _private->originRootObject->deref();
258
259    [super finalize];
260}
261
262+ (BOOL)throwException:(NSString *)exceptionMessage
263{
264    ObjcInstance::setGlobalException(exceptionMessage);
265    return YES;
266}
267
268static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* rootObject, MarkedArgumentBuffer& aList)
269{
270    int i, numObjects = array ? [array count] : 0;
271
272    for (i = 0; i < numObjects; i++) {
273        id anObject = [array objectAtIndex:i];
274        aList.append(convertObjcValueToValue(exec, &anObject, ObjcObjectType, rootObject));
275    }
276}
277
278- (id)callWebScriptMethod:(NSString *)name withArguments:(NSArray *)args
279{
280    if (![self _isSafeScript])
281        return nil;
282
283    JSLock lock(SilenceAssertionsOnly);
284
285    // Look up the function object.
286    ExecState* exec = [self _rootObject]->globalObject()->globalExec();
287    ASSERT(!exec->hadException());
288
289    JSValue function = [self _imp]->get(exec, Identifier(exec, String(name)));
290    CallData callData;
291    CallType callType = function.getCallData(callData);
292    if (callType == CallTypeNone)
293        return nil;
294
295    MarkedArgumentBuffer argList;
296    getListFromNSArray(exec, args, [self _rootObject], argList);
297
298    if (![self _isSafeScript])
299        return nil;
300
301    [self _rootObject]->globalObject()->globalData()->timeoutChecker.start();
302    JSValue result = call(exec, function, callType, callData, [self _imp], argList);
303    [self _rootObject]->globalObject()->globalData()->timeoutChecker.stop();
304
305    if (exec->hadException()) {
306        addExceptionToConsole(exec);
307        result = jsUndefined();
308        exec->clearException();
309    }
310
311    // Convert and return the result of the function call.
312    id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
313
314    _didExecute(self);
315
316    return resultObj;
317}
318
319- (id)evaluateWebScript:(NSString *)script
320{
321    if (![self _isSafeScript])
322        return nil;
323
324    ExecState* exec = [self _rootObject]->globalObject()->globalExec();
325    ASSERT(!exec->hadException());
326
327    JSValue result;
328    JSLock lock(SilenceAssertionsOnly);
329
330    [self _rootObject]->globalObject()->globalData()->timeoutChecker.start();
331    Completion completion = JSC::evaluate([self _rootObject]->globalObject()->globalExec(), [self _rootObject]->globalObject()->globalScopeChain(), makeSource(String(script)));
332    [self _rootObject]->globalObject()->globalData()->timeoutChecker.stop();
333    ComplType type = completion.complType();
334
335    if (type == Normal) {
336        result = completion.value();
337        if (!result)
338            result = jsUndefined();
339    } else
340        result = jsUndefined();
341
342    if (exec->hadException()) {
343        addExceptionToConsole(exec);
344        result = jsUndefined();
345        exec->clearException();
346    }
347
348    id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
349
350    _didExecute(self);
351
352    return resultObj;
353}
354
355- (void)setValue:(id)value forKey:(NSString *)key
356{
357    if (![self _isSafeScript])
358        return;
359
360    ExecState* exec = [self _rootObject]->globalObject()->globalExec();
361    ASSERT(!exec->hadException());
362
363    JSLock lock(SilenceAssertionsOnly);
364
365    PutPropertySlot slot;
366    [self _imp]->put(exec, Identifier(exec, String(key)), convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]), slot);
367
368    if (exec->hadException()) {
369        addExceptionToConsole(exec);
370        exec->clearException();
371    }
372
373    _didExecute(self);
374}
375
376- (id)valueForKey:(NSString *)key
377{
378    if (![self _isSafeScript])
379        return nil;
380
381    ExecState* exec = [self _rootObject]->globalObject()->globalExec();
382    ASSERT(!exec->hadException());
383
384    id resultObj;
385    {
386        // Need to scope this lock to ensure that we release the lock before calling
387        // [super valueForKey:key] which might throw an exception and bypass the JSLock destructor,
388        // leaving the lock permanently held
389        JSLock lock(SilenceAssertionsOnly);
390
391        JSValue result = [self _imp]->get(exec, Identifier(exec, String(key)));
392
393        if (exec->hadException()) {
394            addExceptionToConsole(exec);
395            result = jsUndefined();
396            exec->clearException();
397        }
398
399        resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
400    }
401
402    if ([resultObj isKindOfClass:[WebUndefined class]])
403        resultObj = [super valueForKey:key];    // defaults to throwing an exception
404
405    JSLock lock(SilenceAssertionsOnly);
406    _didExecute(self);
407
408    return resultObj;
409}
410
411- (void)removeWebScriptKey:(NSString *)key
412{
413    if (![self _isSafeScript])
414        return;
415
416    ExecState* exec = [self _rootObject]->globalObject()->globalExec();
417    ASSERT(!exec->hadException());
418
419    JSLock lock(SilenceAssertionsOnly);
420    [self _imp]->deleteProperty(exec, Identifier(exec, String(key)));
421
422    if (exec->hadException()) {
423        addExceptionToConsole(exec);
424        exec->clearException();
425    }
426
427    _didExecute(self);
428}
429
430- (NSString *)stringRepresentation
431{
432    if (![self _isSafeScript]) {
433        // This is a workaround for a gcc 3.3 internal compiler error.
434        return @"Undefined";
435    }
436
437    JSLock lock(SilenceAssertionsOnly);
438    ExecState* exec = [self _rootObject]->globalObject()->globalExec();
439
440    id result = convertValueToObjcValue(exec, [self _imp], ObjcObjectType).objectValue;
441
442    NSString *description = [result description];
443
444    _didExecute(self);
445
446    return description;
447}
448
449- (id)webScriptValueAtIndex:(unsigned)index
450{
451    if (![self _isSafeScript])
452        return nil;
453
454    ExecState* exec = [self _rootObject]->globalObject()->globalExec();
455    ASSERT(!exec->hadException());
456
457    JSLock lock(SilenceAssertionsOnly);
458    JSValue result = [self _imp]->get(exec, index);
459
460    if (exec->hadException()) {
461        addExceptionToConsole(exec);
462        result = jsUndefined();
463        exec->clearException();
464    }
465
466    id resultObj = [WebScriptObject _convertValueToObjcValue:result originRootObject:[self _originRootObject] rootObject:[self _rootObject]];
467
468    _didExecute(self);
469
470    return resultObj;
471}
472
473- (void)setWebScriptValueAtIndex:(unsigned)index value:(id)value
474{
475    if (![self _isSafeScript])
476        return;
477
478    ExecState* exec = [self _rootObject]->globalObject()->globalExec();
479    ASSERT(!exec->hadException());
480
481    JSLock lock(SilenceAssertionsOnly);
482    [self _imp]->put(exec, index, convertObjcValueToValue(exec, &value, ObjcObjectType, [self _rootObject]));
483
484    if (exec->hadException()) {
485        addExceptionToConsole(exec);
486        exec->clearException();
487    }
488
489    _didExecute(self);
490}
491
492- (void)setException:(NSString *)description
493{
494    if (![self _rootObject])
495        return;
496    ObjcInstance::setGlobalException(description, [self _rootObject]->globalObject());
497}
498
499- (JSObjectRef)JSObject
500{
501    if (![self _isSafeScript])
502        return NULL;
503
504    return toRef([self _imp]);
505}
506
507+ (id)_convertValueToObjcValue:(JSValue)value originRootObject:(RootObject*)originRootObject rootObject:(RootObject*)rootObject
508{
509    if (value.isObject()) {
510        JSObject* object = asObject(value);
511        ExecState* exec = rootObject->globalObject()->globalExec();
512        JSLock lock(SilenceAssertionsOnly);
513
514        if (object->classInfo() != &RuntimeObjectImp::s_info) {
515            JSValue runtimeObject = object->get(exec, Identifier(exec, "__apple_runtime_object"));
516            if (runtimeObject && runtimeObject.isObject())
517                object = asObject(runtimeObject);
518        }
519
520        if (object->classInfo() == &RuntimeObjectImp::s_info) {
521            RuntimeObjectImp* imp = static_cast<RuntimeObjectImp*>(object);
522            ObjcInstance *instance = static_cast<ObjcInstance*>(imp->getInternalInstance());
523            if (instance)
524                return instance->getObject();
525            return nil;
526        }
527
528        return [WebScriptObject scriptObjectForJSObject:toRef(object) originRootObject:originRootObject rootObject:rootObject];
529    }
530
531    if (value.isString()) {
532        const UString& u = asString(value)->value();
533        return [NSString stringWithCharacters:u.data() length:u.size()];
534    }
535
536    if (value.isNumber())
537        return [NSNumber numberWithDouble:value.uncheckedGetNumber()];
538
539    if (value.isBoolean())
540        return [NSNumber numberWithBool:value.getBoolean()];
541
542    if (value.isUndefined())
543        return [WebUndefined undefined];
544
545    // jsNull is not returned as NSNull because existing applications do not expect
546    // that return value. Return as nil for compatibility. <rdar://problem/4651318> <rdar://problem/4701626>
547    // Other types (e.g., UnspecifiedType) also return as nil.
548    return nil;
549}
550
551@end
552
553@interface WebScriptObject (WebKitCocoaBindings)
554
555- (id)objectAtIndex:(unsigned)index;
556
557@end
558
559@implementation WebScriptObject (WebKitCocoaBindings)
560
561#if 0
562
563// FIXME: We'd like to add this, but we can't do that until this issue is resolved:
564// http://bugs.webkit.org/show_bug.cgi?id=13129: presence of 'count' method on
565// WebScriptObject breaks Democracy player.
566
567- (unsigned)count
568{
569    id length = [self valueForKey:@"length"];
570    if (![length respondsToSelector:@selector(intValue)])
571        return 0;
572    return [length intValue];
573}
574
575#endif
576
577- (id)objectAtIndex:(unsigned)index
578{
579    return [self webScriptValueAtIndex:index];
580}
581
582@end
583
584@implementation WebUndefined
585
586+ (id)allocWithZone:(NSZone *)unusedZone
587{
588    UNUSED_PARAM(unusedZone);
589
590    static WebUndefined *sharedUndefined = 0;
591    if (!sharedUndefined)
592        sharedUndefined = [super allocWithZone:NULL];
593    return sharedUndefined;
594}
595
596- (NSString *)description
597{
598    return @"undefined";
599}
600
601- (id)initWithCoder:(NSCoder *)unusedCoder
602{
603    UNUSED_PARAM(unusedCoder);
604
605    return self;
606}
607
608- (void)encodeWithCoder:(NSCoder *)unusedCoder
609{
610    UNUSED_PARAM(unusedCoder);
611}
612
613- (id)copyWithZone:(NSZone *)unusedZone
614{
615    UNUSED_PARAM(unusedZone);
616
617    return self;
618}
619
620- (id)retain
621{
622    return self;
623}
624
625- (void)release
626{
627}
628
629- (NSUInteger)retainCount
630{
631    return UINT_MAX;
632}
633
634- (id)autorelease
635{
636    return self;
637}
638
639- (void)dealloc
640{
641    ASSERT(false);
642    return;
643    [super dealloc]; // make -Wdealloc-check happy
644}
645
646+ (WebUndefined *)undefined
647{
648    return [WebUndefined allocWithZone:NULL];
649}
650
651@end
652