Reviewed by Darin Adler.
[WebKit-https.git] / WebCore / bridge / runtime_object.cpp
1 /*
2  * Copyright (C) 2003 Apple Computer, 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 #include "config.h"
27 #include "runtime_object.h"
28
29 #include "runtime_method.h"
30 #include "runtime_root.h"
31
32 using namespace KJS;
33 using namespace Bindings;
34
35 const ClassInfo RuntimeObjectImp::info = { "RuntimeObject", 0, 0 };
36
37 RuntimeObjectImp::RuntimeObjectImp(PassRefPtr<Bindings::Instance> i)
38     : instance(i)
39 {
40     instance->rootObject()->addRuntimeObject(this);
41     Collector::collectOnMainThreadOnly(this);
42 }
43
44 RuntimeObjectImp::~RuntimeObjectImp()
45 {
46     if (instance)
47         instance->rootObject()->removeRuntimeObject(this);
48 }
49
50 void RuntimeObjectImp::invalidate()
51 {
52     ASSERT(instance);
53     instance = 0;
54 }
55
56 JSValue *RuntimeObjectImp::fallbackObjectGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
57 {
58     RuntimeObjectImp *thisObj = static_cast<RuntimeObjectImp *>(slot.slotBase());
59     RefPtr<Bindings::Instance> instance = thisObj->instance;
60
61     if (!instance)
62         return throwInvalidAccessError(exec);
63     
64     instance->begin();
65
66     Class *aClass = instance->getClass();
67     JSValue* result = aClass->fallbackObject(exec, instance.get(), propertyName);
68
69     instance->end();
70             
71     return result;
72 }
73
74 JSValue *RuntimeObjectImp::fieldGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
75 {    
76     RuntimeObjectImp *thisObj = static_cast<RuntimeObjectImp *>(slot.slotBase());
77     RefPtr<Bindings::Instance> instance = thisObj->instance;
78
79     if (!instance)
80         return throwInvalidAccessError(exec);
81     
82     instance->begin();
83
84     Class *aClass = instance->getClass();
85     Field* aField = aClass->fieldNamed(propertyName, instance.get());
86     JSValue *result = instance->getValueOfField(exec, aField); 
87     
88     instance->end();
89             
90     return result;
91 }
92
93 JSValue *RuntimeObjectImp::methodGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
94 {
95     RuntimeObjectImp *thisObj = static_cast<RuntimeObjectImp *>(slot.slotBase());
96     RefPtr<Bindings::Instance> instance = thisObj->instance;
97
98     if (!instance)
99         return throwInvalidAccessError(exec);
100     
101     instance->begin();
102
103     Class *aClass = instance->getClass();
104     MethodList methodList = aClass->methodsNamed(propertyName, instance.get());
105     JSValue *result = new RuntimeMethod(exec, propertyName, methodList);
106
107     instance->end();
108             
109     return result;
110 }
111
112 bool RuntimeObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
113 {
114     if (!instance) {
115         throwInvalidAccessError(exec);
116         return false;
117     }
118     
119     instance->begin();
120     
121     Class *aClass = instance->getClass();
122     
123     if (aClass) {
124         // See if the instance has a field with the specified name.
125         Field *aField = aClass->fieldNamed(propertyName, instance.get());
126         if (aField) {
127             slot.setCustom(this, fieldGetter);
128             instance->end();
129             return true;
130         } else {
131             // Now check if a method with specified name exists, if so return a function object for
132             // that method.
133             MethodList methodList = aClass->methodsNamed(propertyName, instance.get());
134             if (methodList.size() > 0) {
135                 slot.setCustom(this, methodGetter);
136                 
137                 instance->end();
138                 return true;
139             }
140         }
141
142         // Try a fallback object.
143         if (!aClass->fallbackObject(exec, instance.get(), propertyName)->isUndefined()) {
144             slot.setCustom(this, fallbackObjectGetter);
145             instance->end();
146             return true;
147         }
148     }
149         
150     instance->end();
151
152     // don't call superclass, because runtime objects can't have custom properties or a prototype
153     return false;
154 }
155
156 void RuntimeObjectImp::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
157 {
158     if (!instance) {
159         throwInvalidAccessError(exec);
160         return;
161     }
162     
163     RefPtr<Bindings::Instance> protector(instance);
164     instance->begin();
165
166     // Set the value of the property.
167     Field *aField = instance->getClass()->fieldNamed(propertyName, instance.get());
168     if (aField)
169         instance->setValueOfField(exec, aField, value);
170     else if (instance->supportsSetValueOfUndefinedField())
171         instance->setValueOfUndefinedField(exec, propertyName, value);
172
173     instance->end();
174 }
175
176 bool RuntimeObjectImp::deleteProperty(ExecState*, const Identifier&)
177 {
178     // Can never remove a property of a RuntimeObject.
179     return false;
180 }
181
182 JSValue *RuntimeObjectImp::defaultValue(ExecState* exec, JSType hint) const
183 {
184     if (!instance)
185         return throwInvalidAccessError(exec);
186     
187     JSValue *result;
188     
189     RefPtr<Bindings::Instance> protector(instance);
190     instance->begin();
191
192     result = instance->defaultValue(hint);
193     
194     instance->end();
195     
196     return result;
197 }
198     
199 bool RuntimeObjectImp::implementsCall() const
200 {
201     if (!instance)
202         return false;
203     
204     return instance->implementsCall();
205 }
206
207 JSValue *RuntimeObjectImp::callAsFunction(ExecState* exec, JSObject*, const List& args)
208 {
209     if (!instance)
210         return throwInvalidAccessError(exec);
211
212     RefPtr<Bindings::Instance> protector(instance);
213     instance->begin();
214
215     JSValue *aValue = instance->invokeDefaultMethod(exec, args);
216     
217     instance->end();
218     
219     return aValue;
220 }
221
222 void RuntimeObjectImp::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
223 {
224     if (!instance) {
225         throwInvalidAccessError(exec);
226         return;
227     }
228     
229     instance->begin();
230     instance->getPropertyNames(exec, propertyNames);
231     instance->end();
232 }
233
234 JSObject* RuntimeObjectImp::throwInvalidAccessError(ExecState* exec)
235 {
236     return throwError(exec, ReferenceError, "Trying to access object from destroyed plug-in.");
237 }