4447ab0cb44a45fa993727542ae918660388d917
[WebKit-https.git] / Source / WebKit2 / WebProcess / Plugins / Netscape / JSNPObject.cpp
1 /*
2  * Copyright (C) 2010 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 INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "JSNPObject.h"
28
29 #if ENABLE(NETSCAPE_PLUGIN_API)
30
31 #include "JSNPMethod.h"
32 #include "NPJSObject.h"
33 #include "NPRuntimeObjectMap.h"
34 #include "NPRuntimeUtilities.h"
35 #include <JavaScriptCore/Error.h>
36 #include <JavaScriptCore/JSGlobalObject.h>
37 #include <JavaScriptCore/JSLock.h>
38 #include <JavaScriptCore/ObjectPrototype.h>
39 #include <WebCore/IdentifierRep.h>
40 #include <WebCore/JSDOMWindowBase.h>
41 #include <wtf/Assertions.h>
42 #include <wtf/text/WTFString.h>
43
44 using namespace JSC;
45 using namespace WebCore;
46
47 namespace WebKit {
48
49 static NPIdentifier npIdentifierFromIdentifier(PropertyName propertyName)
50 {
51     String name(propertyName.publicName());
52     if (name.isNull())
53         return 0;
54     return static_cast<NPIdentifier>(IdentifierRep::get(name.utf8().data()));
55 }
56
57 const ClassInfo JSNPObject::s_info = { "NPObject", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSNPObject) };
58
59 JSNPObject::JSNPObject(JSGlobalObject* globalObject, Structure* structure, NPRuntimeObjectMap* objectMap, NPObject* npObject)
60     : JSDestructibleObject(globalObject->vm(), structure)
61     , m_objectMap(objectMap)
62     , m_npObject(npObject)
63 {
64     ASSERT(globalObject == structure->globalObject());
65 }
66
67 void JSNPObject::finishCreation(JSGlobalObject* globalObject)
68 {
69     Base::finishCreation(globalObject->vm());
70     ASSERT(inherits(info()));
71
72     // We should never have an NPJSObject inside a JSNPObject.
73     ASSERT(!NPJSObject::isNPJSObject(m_npObject));
74
75     retainNPObject(m_npObject);
76 }
77
78 JSNPObject::~JSNPObject()
79 {
80     if (m_npObject)
81         invalidate();
82 }
83
84 void JSNPObject::destroy(JSCell* cell)
85 {
86     static_cast<JSNPObject*>(cell)->JSNPObject::~JSNPObject();
87 }
88
89 void JSNPObject::invalidate()
90 {
91     ASSERT(m_npObject);
92     ASSERT_GC_OBJECT_INHERITS(this, info());
93
94     releaseNPObject(m_npObject);
95     m_npObject = 0;
96 }
97
98 NPObject* JSNPObject::leakNPObject()
99 {
100     ASSERT(m_npObject);
101
102     NPObject* object = m_npObject;
103     m_npObject = 0;
104     return object;
105 }
106
107 JSValue JSNPObject::callMethod(ExecState* exec, NPIdentifier methodName)
108 {
109     ASSERT_GC_OBJECT_INHERITS(this, info());
110     if (!m_npObject)
111         return throwInvalidAccessError(exec);
112
113     size_t argumentCount = exec->argumentCount();
114     Vector<NPVariant, 8> arguments(argumentCount);
115
116     // Convert all arguments to NPVariants.
117     for (size_t i = 0; i < argumentCount; ++i)
118         m_objectMap->convertJSValueToNPVariant(exec, exec->uncheckedArgument(i), arguments[i]);
119
120     // Calling NPClass::invoke will call into plug-in code, and there's no telling what the plug-in can do.
121     // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 
122     // the call has finished.
123     NPRuntimeObjectMap::PluginProtector protector(m_objectMap);
124
125     bool returnValue;
126     NPVariant result;
127     VOID_TO_NPVARIANT(result);
128     
129     {
130         JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
131         returnValue = m_npObject->_class->invoke(m_npObject, methodName, arguments.data(), argumentCount, &result);
132         NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec);
133     }
134
135     // Release all arguments;
136     for (size_t i = 0; i < argumentCount; ++i)
137         releaseNPVariantValue(&arguments[i]);
138
139     if (!returnValue)
140         exec->vm().throwException(exec, createError(exec, "Error calling method on NPObject."));
141
142     JSValue propertyValue = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result);
143     releaseNPVariantValue(&result);
144     return propertyValue;
145 }
146
147 JSC::JSValue JSNPObject::callObject(JSC::ExecState* exec)
148 {
149     ASSERT_GC_OBJECT_INHERITS(this, info());
150     if (!m_npObject)
151         return throwInvalidAccessError(exec);
152
153     size_t argumentCount = exec->argumentCount();
154     Vector<NPVariant, 8> arguments(argumentCount);
155     
156     // Convert all arguments to NPVariants.
157     for (size_t i = 0; i < argumentCount; ++i)
158         m_objectMap->convertJSValueToNPVariant(exec, exec->uncheckedArgument(i), arguments[i]);
159
160     // Calling NPClass::invokeDefault will call into plug-in code, and there's no telling what the plug-in can do.
161     // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 
162     // the call has finished.
163     NPRuntimeObjectMap::PluginProtector protector(m_objectMap);
164     
165     bool returnValue;
166     NPVariant result;
167     VOID_TO_NPVARIANT(result);
168
169     {
170         JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
171         returnValue = m_npObject->_class->invokeDefault(m_npObject, arguments.data(), argumentCount, &result);
172         NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec);
173     }
174
175     // Release all arguments;
176     for (size_t i = 0; i < argumentCount; ++i)
177         releaseNPVariantValue(&arguments[i]);
178
179     if (!returnValue)
180         exec->vm().throwException(exec, createError(exec, "Error calling method on NPObject."));
181
182     JSValue propertyValue = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result);
183     releaseNPVariantValue(&result);
184     return propertyValue;
185 }
186
187 JSValue JSNPObject::callConstructor(ExecState* exec)
188 {
189     ASSERT_GC_OBJECT_INHERITS(this, info());
190     if (!m_npObject)
191         return throwInvalidAccessError(exec);
192
193     size_t argumentCount = exec->argumentCount();
194     Vector<NPVariant, 8> arguments(argumentCount);
195
196     // Convert all arguments to NPVariants.
197     for (size_t i = 0; i < argumentCount; ++i)
198         m_objectMap->convertJSValueToNPVariant(exec, exec->uncheckedArgument(i), arguments[i]);
199
200     // Calling NPClass::construct will call into plug-in code, and there's no telling what the plug-in can do.
201     // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 
202     // the call has finished.
203     NPRuntimeObjectMap::PluginProtector protector(m_objectMap);
204     
205     bool returnValue;
206     NPVariant result;
207     VOID_TO_NPVARIANT(result);
208     
209     {
210         JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
211         returnValue = m_npObject->_class->construct(m_npObject, arguments.data(), argumentCount, &result);
212         NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec);
213     }
214
215     if (!returnValue)
216         exec->vm().throwException(exec, createError(exec, "Error calling method on NPObject."));
217     
218     JSValue value = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result);
219     releaseNPVariantValue(&result);
220     return value;
221 }
222
223 static EncodedJSValue JSC_HOST_CALL callNPJSObject(ExecState* exec)
224 {
225     JSObject* object = exec->callee();
226     ASSERT(object->inherits(JSNPObject::info()));
227
228     return JSValue::encode(static_cast<JSNPObject*>(object)->callObject(exec));
229 }
230
231 JSC::CallType JSNPObject::getCallData(JSC::JSCell* cell, JSC::CallData& callData)
232 {
233     JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell);
234     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
235     if (!thisObject->m_npObject || !thisObject->m_npObject->_class->invokeDefault)
236         return CallTypeNone;
237
238     callData.native.function = callNPJSObject;
239     return CallTypeHost;
240 }
241
242 static EncodedJSValue JSC_HOST_CALL constructWithConstructor(ExecState* exec)
243 {
244     JSObject* constructor = exec->callee();
245     ASSERT(constructor->inherits(JSNPObject::info()));
246
247     return JSValue::encode(static_cast<JSNPObject*>(constructor)->callConstructor(exec));
248 }
249
250 ConstructType JSNPObject::getConstructData(JSCell* cell, ConstructData& constructData)
251 {
252     JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell);
253     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
254     if (!thisObject->m_npObject || !thisObject->m_npObject->_class->construct)
255         return ConstructTypeNone;
256
257     constructData.native.function = constructWithConstructor;
258     return ConstructTypeHost;
259 }
260
261 bool JSNPObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
262 {
263     JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(object);
264     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
265     if (!thisObject->m_npObject) {
266         throwInvalidAccessError(exec);
267         return false;
268     }
269     
270     NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName);
271
272     // Calling NPClass::invoke will call into plug-in code, and there's no telling what the plug-in can do.
273     // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 
274     // the call has finished.
275     NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap);
276
277     // First, check if the NPObject has a property with this name.
278     if (thisObject->m_npObject->_class->hasProperty && thisObject->m_npObject->_class->hasProperty(thisObject->m_npObject, npIdentifier)) {
279         slot.setCustom(thisObject, DontDelete, thisObject->propertyGetter);
280         return true;
281     }
282
283     // Second, check if the NPObject has a method with this name.
284     if (thisObject->m_npObject->_class->hasMethod && thisObject->m_npObject->_class->hasMethod(thisObject->m_npObject, npIdentifier)) {
285         slot.setCustom(thisObject, DontDelete | ReadOnly, thisObject->methodGetter);
286         return true;
287     }
288     
289     return false;
290 }
291
292 void JSNPObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot&)
293 {
294     JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell);
295     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
296     if (!thisObject->m_npObject) {
297         throwInvalidAccessError(exec);
298         return;
299     }
300
301     NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName);
302     
303     if (!thisObject->m_npObject->_class->hasProperty || !thisObject->m_npObject->_class->hasProperty(thisObject->m_npObject, npIdentifier)) {
304         // FIXME: Should we throw an exception here?
305         return;
306     }
307
308     if (!thisObject->m_npObject->_class->setProperty)
309         return;
310
311     NPVariant variant;
312     thisObject->m_objectMap->convertJSValueToNPVariant(exec, value, variant);
313
314     // Calling NPClass::setProperty will call into plug-in code, and there's no telling what the plug-in can do.
315     // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 
316     // the call has finished.
317     NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap);
318
319     {
320         JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
321         thisObject->m_npObject->_class->setProperty(thisObject->m_npObject, npIdentifier, &variant);
322
323         NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec);
324
325         // FIXME: Should we throw an exception if setProperty returns false?
326     }
327
328     releaseNPVariantValue(&variant);
329 }
330
331 bool JSNPObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
332 {
333     return jsCast<JSNPObject*>(cell)->deleteProperty(exec, npIdentifierFromIdentifier(propertyName));
334 }
335
336 bool JSNPObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
337 {
338     return jsCast<JSNPObject*>(cell)->deleteProperty(exec, static_cast<NPIdentifier>(IdentifierRep::get(propertyName)));
339 }
340
341 bool JSNPObject::deleteProperty(ExecState* exec, NPIdentifier propertyName)
342 {
343     ASSERT_GC_OBJECT_INHERITS(this, info());
344     if (!m_npObject) {
345         throwInvalidAccessError(exec);
346         return false;
347     }
348
349     if (!m_npObject->_class->removeProperty) {
350         // FIXME: Should we throw an exception here?
351         return false;
352     }
353
354     // Calling NPClass::setProperty will call into plug-in code, and there's no telling what the plug-in can do.
355     // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 
356     // the call has finished.
357     NPRuntimeObjectMap::PluginProtector protector(m_objectMap);
358
359     {
360         JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
361
362         // FIXME: Should we throw an exception if removeProperty returns false?
363         if (!m_npObject->_class->removeProperty(m_npObject, propertyName))
364             return false;
365
366         NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec);
367     }
368
369     return true;
370 }
371
372 void JSNPObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNameArray, EnumerationMode)
373 {
374     JSNPObject* thisObject = jsCast<JSNPObject*>(object);
375     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
376     if (!thisObject->m_npObject) {
377         throwInvalidAccessError(exec);
378         return;
379     }
380
381     if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(thisObject->m_npObject->_class) || !thisObject->m_npObject->_class->enumerate)
382         return;
383
384     NPIdentifier* identifiers = 0;
385     uint32_t identifierCount = 0;
386     
387     // Calling NPClass::enumerate will call into plug-in code, and there's no telling what the plug-in can do.
388     // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 
389     // the call has finished.
390     NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap);
391     
392     {
393         JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
394
395         // FIXME: Should we throw an exception if enumerate returns false?
396         if (!thisObject->m_npObject->_class->enumerate(thisObject->m_npObject, &identifiers, &identifierCount))
397             return;
398
399         NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec);
400     }
401
402     for (uint32_t i = 0; i < identifierCount; ++i) {
403         IdentifierRep* identifierRep = static_cast<IdentifierRep*>(identifiers[i]);
404         
405         Identifier identifier;
406         if (identifierRep->isString()) {
407             const char* string = identifierRep->string();
408             int length = strlen(string);
409             
410             identifier = Identifier(exec, String::fromUTF8WithLatin1Fallback(string, length).impl());
411         } else
412             identifier = Identifier::from(exec, identifierRep->number());
413
414         propertyNameArray.add(identifier);
415     }
416
417     npnMemFree(identifiers);
418 }
419
420 JSValue JSNPObject::propertyGetter(ExecState* exec, JSValue slotBase, PropertyName propertyName)
421 {
422     JSNPObject* thisObj = static_cast<JSNPObject*>(asObject(slotBase));
423     ASSERT_GC_OBJECT_INHERITS(thisObj, info());
424     
425     if (!thisObj->m_npObject)
426         return throwInvalidAccessError(exec);
427
428     if (!thisObj->m_npObject->_class->getProperty)
429         return jsUndefined();
430
431     NPVariant result;
432     VOID_TO_NPVARIANT(result);
433
434     // Calling NPClass::getProperty will call into plug-in code, and there's no telling what the plug-in can do.
435     // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 
436     // the call has finished.
437     NPRuntimeObjectMap::PluginProtector protector(thisObj->m_objectMap);
438     
439     bool returnValue;
440     {
441         JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
442         NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName);
443         returnValue = thisObj->m_npObject->_class->getProperty(thisObj->m_npObject, npIdentifier, &result);
444         
445         NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec);
446     }
447
448     if (!returnValue)
449         return jsUndefined();
450
451     JSValue propertyValue = thisObj->m_objectMap->convertNPVariantToJSValue(exec, thisObj->globalObject(), result);
452     releaseNPVariantValue(&result);
453     return propertyValue;
454 }
455
456 JSValue JSNPObject::methodGetter(ExecState* exec, JSValue slotBase, PropertyName propertyName)
457 {
458     JSNPObject* thisObj = static_cast<JSNPObject*>(asObject(slotBase));
459     ASSERT_GC_OBJECT_INHERITS(thisObj, info());
460     
461     if (!thisObj->m_npObject)
462         return throwInvalidAccessError(exec);
463
464     NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName);
465     return JSNPMethod::create(exec, thisObj->globalObject(), propertyName.publicName(), npIdentifier);
466 }
467
468 JSObject* JSNPObject::throwInvalidAccessError(ExecState* exec)
469 {
470     return exec->vm().throwException(exec, createReferenceError(exec, "Trying to access object from destroyed plug-in."));
471 }
472
473 } // namespace WebKit
474
475 #endif // ENABLE(NETSCAPE_PLUGIN_API)