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