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