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