Reviewed by Maciej.
[WebKit-https.git] / JavaScriptGlue / UserObjectImp.cpp
1 #include "UserObjectImp.h"
2 #include "JavaScriptCore/reference_list.h"
3
4 const ClassInfo UserObjectImp::info = {"UserObject", 0, 0, 0};
5
6 class UserObjectPrototypeImp : public UserObjectImp {
7   public:
8     UserObjectPrototypeImp();
9     static UserObjectPrototypeImp* GlobalUserObjectPrototypeImp();
10   private:
11     static UserObjectPrototypeImp* sUserObjectPrototypeImp;
12 };
13
14 UserObjectPrototypeImp* UserObjectPrototypeImp::sUserObjectPrototypeImp = 0;
15
16 UserObjectPrototypeImp::UserObjectPrototypeImp()
17   : UserObjectImp()
18 {
19 }
20
21 UserObjectPrototypeImp* UserObjectPrototypeImp::GlobalUserObjectPrototypeImp()
22 {
23     if (!sUserObjectPrototypeImp)
24     {
25             sUserObjectPrototypeImp  = new UserObjectPrototypeImp();
26             static ProtectedPtr<UserObjectPrototypeImp> protectPrototype;
27     }
28     return sUserObjectPrototypeImp;
29 }
30
31
32 UserObjectImp::UserObjectImp(): ObjectImp(), fJSUserObject(0)
33 {
34 }
35
36 UserObjectImp::UserObjectImp(JSUserObject* userObject) :
37     ObjectImp(UserObjectPrototypeImp::GlobalUserObjectPrototypeImp()),
38     fJSUserObject((JSUserObject*)userObject->Retain())
39 {
40 }
41
42 UserObjectImp::~UserObjectImp()
43 {
44     if (fJSUserObject)
45         fJSUserObject->Release();
46 }
47
48 const ClassInfo * UserObjectImp::classInfo() const
49 {
50     return &info;
51 }
52
53 bool UserObjectImp::implementsCall() const
54 {
55     return fJSUserObject ? fJSUserObject->ImplementsCall() : false;
56 }
57
58 ValueImp *UserObjectImp::callAsFunction(ExecState *exec, ObjectImp *thisObj, const List &args)
59 {
60     ValueImp *result = jsUndefined();
61     JSUserObject* jsThisObj = KJSValueToJSObject(thisObj, exec);
62     if (jsThisObj) {
63         CFIndex argCount = args.size();
64         CFArrayCallBacks arrayCallBacks;
65         JSTypeGetCFArrayCallBacks(&arrayCallBacks);
66         CFMutableArrayRef jsArgs = CFArrayCreateMutable(0, 0, &arrayCallBacks);
67         if (jsArgs) {
68             for (CFIndex i = 0; i < argCount; i++) {
69                 JSUserObject* jsArg = KJSValueToJSObject(args[i], exec);
70                 CFArrayAppendValue(jsArgs, (void*)jsArg);
71                 jsArg->Release();
72             }
73         }
74
75         JSUserObject* jsResult;
76         { // scope
77             JSLock::DropAllLocks dropLocks;
78
79             // implementsCall should have guarded against a NULL fJSUserObject.
80             assert(fJSUserObject);
81             jsResult = fJSUserObject->CallFunction(jsThisObj, jsArgs);
82         }
83
84         if (jsResult) {
85             result = JSObjectKJSValue(jsResult);
86             jsResult->Release();
87         }
88
89         ReleaseCFType(jsArgs);
90         jsThisObj->Release();
91     }
92     return result;
93 }
94
95
96 ReferenceList UserObjectImp::propList(ExecState *exec, bool recursive)
97 {
98     ReferenceList list = ObjectImp::propList(exec, recursive);
99     JSUserObject* ptr = GetJSUserObject();
100     if (ptr) {
101         CFArrayRef cfPropertyNames = ptr->CopyPropertyNames();
102         if (cfPropertyNames) {
103             CFIndex count = CFArrayGetCount(cfPropertyNames);
104             CFIndex i;
105             for (i = 0; i < count; i++) {
106                 CFStringRef propertyName = (CFStringRef)CFArrayGetValueAtIndex(cfPropertyNames, i);
107                 list.append(Reference(this, CFStringToIdentifier(propertyName)));
108             }
109             CFRelease(cfPropertyNames);
110         }
111     }
112
113     return list;
114 }
115
116 ValueImp *UserObjectImp::userObjectGetter(ExecState *, const Identifier& propertyName, const PropertySlot& slot)
117 {
118     UserObjectImp *thisObj = static_cast<UserObjectImp *>(slot.slotBase());
119     // getOwnPropertySlot should have guarded against a null fJSUserObject.
120     assert(thisObj->fJSUserObject);
121     
122     CFStringRef cfPropName = IdentifierToCFString(propertyName);
123     JSUserObject *jsResult = thisObj->fJSUserObject->CopyProperty(cfPropName);
124     ReleaseCFType(cfPropName);
125     ValueImp *result = JSObjectKJSValue(jsResult);
126     jsResult->Release();
127
128     return result;
129 }
130
131 bool UserObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
132 {
133     if (!fJSUserObject)
134         return false;
135
136     CFStringRef cfPropName = IdentifierToCFString(propertyName);
137     JSUserObject *jsResult = fJSUserObject->CopyProperty(cfPropName);
138     ReleaseCFType(cfPropName);
139     if (jsResult) {
140         slot.setCustom(this, userObjectGetter);
141         jsResult->Release();
142         return true;
143     } else {
144         ValueImp *kjsValue = toPrimitive(exec);
145         if (kjsValue->type() != NullType && kjsValue->type() != UndefinedType) {
146             ObjectImp *kjsObject = kjsValue->toObject(exec);
147             if (kjsObject->getPropertySlot(exec, propertyName, slot))
148                 return true;
149         }
150     }
151     return ObjectImp::getOwnPropertySlot(exec, propertyName, slot);
152 }
153
154 void UserObjectImp::put(ExecState *exec, const Identifier &propertyName, ValueImp *value, int attr)
155 {
156     if (!fJSUserObject)
157         return;
158     
159     CFStringRef cfPropName = IdentifierToCFString(propertyName);
160     JSUserObject *jsValueObj = KJSValueToJSObject(value, exec);
161
162     fJSUserObject->SetProperty(cfPropName, jsValueObj);
163
164     if (jsValueObj) jsValueObj->Release();
165     ReleaseCFType(cfPropName);
166 }
167
168 JSUserObject* UserObjectImp::GetJSUserObject() const
169 {
170     return fJSUserObject;
171 }
172
173 ValueImp *UserObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
174 {
175     ValueImp *result = jsUndefined();
176     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
177     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
178     if (cfValue) {
179         CFTypeID cfType = CFGetTypeID(cfValue);  // toPrimitive
180         if (cfValue == GetCFNull()) {
181             result = jsNull();
182         }
183         else if (cfType == CFBooleanGetTypeID()) {
184             if (cfValue == kCFBooleanTrue) {
185                 result = jsBoolean(true);
186             } else {
187                 result = jsBoolean(false);
188             }
189         } else if (cfType == CFStringGetTypeID()) {
190             result = jsString(CFStringToUString((CFStringRef)cfValue));
191         } else if (cfType == CFNumberGetTypeID()) {
192             double d = 0.0;
193             CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d);
194             result = jsNumber(d);
195         } else if (cfType == CFURLGetTypeID()) {
196             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
197             if (absURL) {
198                 result = jsString(CFStringToUString(CFURLGetString(absURL)));
199                 ReleaseCFType(absURL);
200             }
201         }
202         ReleaseCFType(cfValue);
203     }
204     if (jsObjPtr)
205         jsObjPtr->Release();
206     return result;
207 }
208
209
210 bool UserObjectImp::toBoolean(ExecState *exec) const
211 {
212     bool result = false;
213     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
214     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
215     if (cfValue)
216     {
217         CFTypeID cfType = CFGetTypeID(cfValue);  // toPrimitive
218         if (cfValue == GetCFNull())
219         {
220             //
221         }
222         else if (cfType == CFBooleanGetTypeID())
223         {
224             if (cfValue == kCFBooleanTrue)
225             {
226                 result = true;
227             }
228         }
229         else if (cfType == CFStringGetTypeID())
230         {
231             if (CFStringGetLength((CFStringRef)cfValue))
232             {
233                 result = true;
234             }
235         }
236         else if (cfType == CFNumberGetTypeID())
237         {
238             if (cfValue != kCFNumberNaN)
239             {
240                 double d;
241                 if (CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d))
242                 {
243                     if (d != 0)
244                     {
245                         result = true;
246                     }
247                 }
248             }
249         }
250         else if (cfType == CFArrayGetTypeID())
251         {
252             if (CFArrayGetCount((CFArrayRef)cfValue))
253             {
254                 result = true;
255             }
256         }
257         else if (cfType == CFDictionaryGetTypeID())
258         {
259             if (CFDictionaryGetCount((CFDictionaryRef)cfValue))
260             {
261                 result = true;
262             }
263         }
264         else if (cfType == CFSetGetTypeID())
265         {
266             if (CFSetGetCount((CFSetRef)cfValue))
267             {
268                 result = true;
269             }
270         }
271         else if (cfType == CFURLGetTypeID())
272         {
273             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
274             if (absURL)
275             {
276                 CFStringRef cfStr = CFURLGetString(absURL);
277                 if (cfStr && CFStringGetLength(cfStr))
278                 {
279                     result = true;
280                 }
281                 ReleaseCFType(absURL);
282             }
283         }
284     }
285     if (jsObjPtr) jsObjPtr->Release();
286     ReleaseCFType(cfValue);
287     return result;
288 }
289
290 double UserObjectImp::toNumber(ExecState *exec) const
291 {
292     double result = 0;
293     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
294     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
295     if (cfValue)
296     {
297         CFTypeID cfType = CFGetTypeID(cfValue);
298
299         if (cfValue == GetCFNull())
300         {
301             //
302         }
303         else if (cfType == CFBooleanGetTypeID())
304         {
305             if (cfValue == kCFBooleanTrue)
306             {
307                 result = 1;
308             }
309         }
310         else if (cfType == CFStringGetTypeID())
311         {
312             result = CFStringGetDoubleValue((CFStringRef)cfValue);
313         }
314         else if (cfType == CFNumberGetTypeID())
315         {
316             CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &result);
317         }
318     }
319     ReleaseCFType(cfValue);
320     if (jsObjPtr) jsObjPtr->Release();
321     return result;
322 }
323
324 UString UserObjectImp::toString(ExecState *exec) const
325 {
326     UString result;
327     JSUserObject* jsObjPtr = KJSValueToJSObject(toObject(exec), exec);
328     CFTypeRef cfValue = jsObjPtr ? jsObjPtr->CopyCFValue() : 0;
329     if (cfValue)
330     {
331         CFTypeID cfType = CFGetTypeID(cfValue);
332         if (cfValue == GetCFNull())
333         {
334             //
335         }
336         else if (cfType == CFBooleanGetTypeID())
337         {
338             if (cfValue == kCFBooleanTrue)
339             {
340                 result = "true";
341             }
342             else
343             {
344                 result = "false";
345             }
346         }
347         else if (cfType == CFStringGetTypeID())
348         {
349             result = CFStringToUString((CFStringRef)cfValue);
350         }
351         else if (cfType == CFNumberGetTypeID())
352         {
353             if (cfValue == kCFNumberNaN)
354             {
355                 result = "Nan";
356             }
357             else if (CFNumberCompare(kCFNumberPositiveInfinity, (CFNumberRef)cfValue, 0) == 0)
358             {
359                 result = "Infinity";
360             }
361             else if (CFNumberCompare(kCFNumberNegativeInfinity, (CFNumberRef)cfValue, 0) == 0)
362             {
363                 result = "-Infinity";
364             }
365             else
366             {
367                 CFStringRef cfNumStr = 0;
368                 if (CFNumberIsFloatType((CFNumberRef)cfValue))
369                 {
370                     double d = 0;
371                     CFNumberGetValue((CFNumberRef)cfValue, kCFNumberDoubleType, &d);
372                     cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%f"), (float)d);
373                 }
374                 else
375                 {
376                     int i = 0;
377                     CFNumberGetValue((CFNumberRef)cfValue, kCFNumberIntType, &i);
378                     cfNumStr = CFStringCreateWithFormat(0, 0, CFSTR("%d"), (int)i);
379                 }
380
381                 if (cfNumStr)
382                 {
383                     result = CFStringToUString(cfNumStr);
384                     ReleaseCFType(cfNumStr);
385                 }
386             }
387         }
388         else if (cfType == CFArrayGetTypeID())
389         {
390             //
391         }
392         else if (cfType == CFDictionaryGetTypeID())
393         {
394             //
395         }
396         else if (cfType == CFSetGetTypeID())
397         {
398             //
399         }
400         else if (cfType == CFURLGetTypeID())
401         {
402             CFURLRef absURL = CFURLCopyAbsoluteURL((CFURLRef)cfValue);
403             if (absURL)
404             {
405                 CFStringRef cfStr = CFURLGetString(absURL);
406                 if (cfStr)
407                 {
408                     result = CFStringToUString(cfStr);
409                 }
410                 ReleaseCFType(absURL);
411             }
412         }
413     }
414     ReleaseCFType(cfValue);
415     if (jsObjPtr) jsObjPtr->Release();
416     return result;
417 }
418
419 void UserObjectImp::mark()
420 {
421     ObjectImp::mark();
422     if (fJSUserObject)
423         fJSUserObject->Mark();
424 }