Home | History | Annotate | Download | only in v8
      1 /*
      2 * Copyright (C) 2006, 2007, 2008, 2009 Google 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 are
      6 * met:
      7 *
      8 *     * Redistributions of source code must retain the above copyright
      9 * notice, this list of conditions and the following disclaimer.
     10 *     * Redistributions in binary form must reproduce the above
     11 * copyright notice, this list of conditions and the following disclaimer
     12 * in the documentation and/or other materials provided with the
     13 * distribution.
     14 *     * Neither the name of Google Inc. nor the names of its
     15 * contributors may be used to endorse or promote products derived from
     16 * this software without specific prior written permission.
     17 *
     18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 */
     30 
     31 #include "config.h"
     32 
     33 #include "V8NPObject.h"
     34 
     35 #include "HTMLPlugInElement.h"
     36 #include "NPV8Object.h"
     37 #include "V8DOMMap.h"
     38 #include "V8HTMLAppletElement.h"
     39 #include "V8HTMLEmbedElement.h"
     40 #include "V8HTMLObjectElement.h"
     41 #include "V8Helpers.h"
     42 #include "V8NPUtils.h"
     43 #include "V8Proxy.h"
     44 #include "npruntime_impl.h"
     45 #include "npruntime_priv.h"
     46 #include <wtf/OwnArrayPtr.h>
     47 
     48 namespace WebCore {
     49 
     50 enum InvokeFunctionType {
     51     InvokeMethod = 1,
     52     InvokeConstruct = 2,
     53     InvokeDefault = 3
     54 };
     55 
     56 struct IdentifierRep {
     57     int number() const { return m_isString ? 0 : m_value.m_number; }
     58     const char* string() const { return m_isString ? m_value.m_string : 0; }
     59 
     60     union {
     61         const char* m_string;
     62         int m_number;
     63     } m_value;
     64     bool m_isString;
     65 };
     66 
     67 // FIXME: need comments.
     68 // Params: holder could be HTMLEmbedElement or NPObject
     69 static v8::Handle<v8::Value> npObjectInvokeImpl(const v8::Arguments& args, InvokeFunctionType functionId)
     70 {
     71     NPObject* npObject;
     72 
     73     // These three types are subtypes of HTMLPlugInElement.
     74     if (V8HTMLAppletElement::HasInstance(args.Holder()) || V8HTMLEmbedElement::HasInstance(args.Holder())
     75         || V8HTMLObjectElement::HasInstance(args.Holder())) {
     76         // The holder object is a subtype of HTMLPlugInElement.
     77         HTMLPlugInElement* element;
     78         if (V8HTMLAppletElement::HasInstance(args.Holder()))
     79             element = V8HTMLAppletElement::toNative(args.Holder());
     80         else if (V8HTMLEmbedElement::HasInstance(args.Holder()))
     81             element = V8HTMLEmbedElement::toNative(args.Holder());
     82         else
     83             element = V8HTMLObjectElement::toNative(args.Holder());
     84         ScriptInstance scriptInstance = element->getInstance();
     85         if (scriptInstance)
     86             npObject = v8ObjectToNPObject(scriptInstance->instance());
     87         else
     88             npObject = 0;
     89     } else {
     90         // The holder object is not a subtype of HTMLPlugInElement, it must be an NPObject which has three
     91         // internal fields.
     92         if (args.Holder()->InternalFieldCount() != npObjectInternalFieldCount)
     93           return throwError("NPMethod called on non-NPObject", V8Proxy::ReferenceError);
     94 
     95         npObject = v8ObjectToNPObject(args.Holder());
     96     }
     97 
     98     // Verify that our wrapper wasn't using a NPObject which has already been deleted.
     99     if (!npObject || !_NPN_IsAlive(npObject))
    100         return throwError("NPObject deleted", V8Proxy::ReferenceError);
    101 
    102     // Wrap up parameters.
    103     int numArgs = args.Length();
    104     OwnArrayPtr<NPVariant> npArgs = adoptArrayPtr(new NPVariant[numArgs]);
    105 
    106     for (int i = 0; i < numArgs; i++)
    107         convertV8ObjectToNPVariant(args[i], npObject, &npArgs[i]);
    108 
    109     NPVariant result;
    110     VOID_TO_NPVARIANT(result);
    111 
    112     bool retval = true;
    113     switch (functionId) {
    114     case InvokeMethod:
    115         if (npObject->_class->invoke) {
    116             v8::Handle<v8::String> functionName(v8::String::Cast(*args.Data()));
    117             NPIdentifier identifier = getStringIdentifier(functionName);
    118             retval = npObject->_class->invoke(npObject, identifier, npArgs.get(), numArgs, &result);
    119         }
    120         break;
    121     case InvokeConstruct:
    122         if (npObject->_class->construct)
    123             retval = npObject->_class->construct(npObject, npArgs.get(), numArgs, &result);
    124         break;
    125     case InvokeDefault:
    126         if (npObject->_class->invokeDefault)
    127             retval = npObject->_class->invokeDefault(npObject, npArgs.get(), numArgs, &result);
    128         break;
    129     default:
    130         break;
    131     }
    132 
    133     if (!retval)
    134         throwError("Error calling method on NPObject.", V8Proxy::GeneralError);
    135 
    136     for (int i = 0; i < numArgs; i++)
    137         _NPN_ReleaseVariantValue(&npArgs[i]);
    138 
    139     // Unwrap return values.
    140     v8::Handle<v8::Value> returnValue = convertNPVariantToV8Object(&result, npObject);
    141     _NPN_ReleaseVariantValue(&result);
    142 
    143     return returnValue;
    144 }
    145 
    146 
    147 v8::Handle<v8::Value> npObjectMethodHandler(const v8::Arguments& args)
    148 {
    149     return npObjectInvokeImpl(args, InvokeMethod);
    150 }
    151 
    152 
    153 v8::Handle<v8::Value> npObjectInvokeDefaultHandler(const v8::Arguments& args)
    154 {
    155     if (args.IsConstructCall())
    156         return npObjectInvokeImpl(args, InvokeConstruct);
    157 
    158     return npObjectInvokeImpl(args, InvokeDefault);
    159 }
    160 
    161 
    162 static void weakTemplateCallback(v8::Persistent<v8::Value>, void* parameter);
    163 
    164 // NPIdentifier is PrivateIdentifier*.
    165 static WeakReferenceMap<PrivateIdentifier, v8::FunctionTemplate> staticTemplateMap(&weakTemplateCallback);
    166 
    167 static void weakTemplateCallback(v8::Persistent<v8::Value> object, void* parameter)
    168 {
    169     PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(parameter);
    170     ASSERT(identifier);
    171     ASSERT(staticTemplateMap.contains(identifier));
    172 
    173     staticTemplateMap.forget(identifier);
    174 }
    175 
    176 
    177 static v8::Handle<v8::Value> npObjectGetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> key)
    178 {
    179     NPObject* npObject = v8ObjectToNPObject(self);
    180 
    181     // Verify that our wrapper wasn't using a NPObject which
    182     // has already been deleted.
    183     if (!npObject || !_NPN_IsAlive(npObject))
    184         return throwError("NPObject deleted", V8Proxy::ReferenceError);
    185 
    186 
    187     if (npObject->_class->hasProperty && npObject->_class->hasProperty(npObject, identifier)
    188         && npObject->_class->getProperty) {
    189 
    190         NPVariant result;
    191         VOID_TO_NPVARIANT(result);
    192         if (!npObject->_class->getProperty(npObject, identifier, &result))
    193             return v8::Handle<v8::Value>();
    194 
    195         v8::Handle<v8::Value> returnValue = convertNPVariantToV8Object(&result, npObject);
    196         _NPN_ReleaseVariantValue(&result);
    197         return returnValue;
    198 
    199     }
    200 
    201     if (key->IsString() && npObject->_class->hasMethod && npObject->_class->hasMethod(npObject, identifier)) {
    202         PrivateIdentifier* id = static_cast<PrivateIdentifier*>(identifier);
    203         v8::Persistent<v8::FunctionTemplate> functionTemplate = staticTemplateMap.get(id);
    204         // Cache templates using identifier as the key.
    205         if (functionTemplate.IsEmpty()) {
    206             // Create a new template.
    207             v8::Local<v8::FunctionTemplate> temp = v8::FunctionTemplate::New();
    208             temp->SetCallHandler(npObjectMethodHandler, key);
    209             functionTemplate = v8::Persistent<v8::FunctionTemplate>::New(temp);
    210             staticTemplateMap.set(id, functionTemplate);
    211         }
    212 
    213         // FunctionTemplate caches function for each context.
    214         v8::Local<v8::Function> v8Function = functionTemplate->GetFunction();
    215         v8Function->SetName(v8::Handle<v8::String>::Cast(key));
    216         return v8Function;
    217     }
    218 
    219     return v8::Handle<v8::Value>();
    220 }
    221 
    222 v8::Handle<v8::Value> npObjectNamedPropertyGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
    223 {
    224     NPIdentifier identifier = getStringIdentifier(name);
    225     return npObjectGetProperty(info.Holder(), identifier, name);
    226 }
    227 
    228 v8::Handle<v8::Value> npObjectIndexedPropertyGetter(uint32_t index, const v8::AccessorInfo& info)
    229 {
    230     NPIdentifier identifier = _NPN_GetIntIdentifier(index);
    231     return npObjectGetProperty(info.Holder(), identifier, v8::Number::New(index));
    232 }
    233 
    234 v8::Handle<v8::Value> npObjectGetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name)
    235 {
    236     NPIdentifier identifier = getStringIdentifier(name);
    237     return npObjectGetProperty(self, identifier, name);
    238 }
    239 
    240 v8::Handle<v8::Value> npObjectGetIndexedProperty(v8::Local<v8::Object> self, uint32_t index)
    241 {
    242     NPIdentifier identifier = _NPN_GetIntIdentifier(index);
    243     return npObjectGetProperty(self, identifier, v8::Number::New(index));
    244 }
    245 
    246 v8::Handle<v8::Integer> npObjectQueryProperty(v8::Local<v8::String> name, const v8::AccessorInfo& info)
    247 {
    248     NPIdentifier identifier = getStringIdentifier(name);
    249     return npObjectGetProperty(info.Holder(), identifier, name).IsEmpty() ? v8::Handle<v8::Integer>() : v8::Integer::New(v8::None);
    250 }
    251 
    252 static v8::Handle<v8::Value> npObjectSetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> value)
    253 {
    254     NPObject* npObject = v8ObjectToNPObject(self);
    255 
    256     // Verify that our wrapper wasn't using a NPObject which has already been deleted.
    257     if (!npObject || !_NPN_IsAlive(npObject)) {
    258         throwError("NPObject deleted", V8Proxy::ReferenceError);
    259         return value;  // Intercepted, but an exception was thrown.
    260     }
    261 
    262     if (npObject->_class->hasProperty && npObject->_class->hasProperty(npObject, identifier)
    263         && npObject->_class->setProperty) {
    264 
    265         NPVariant npValue;
    266         VOID_TO_NPVARIANT(npValue);
    267         convertV8ObjectToNPVariant(value, npObject, &npValue);
    268         bool success = npObject->_class->setProperty(npObject, identifier, &npValue);
    269         _NPN_ReleaseVariantValue(&npValue);
    270         if (success)
    271             return value; // Intercept the call.
    272     }
    273     return notHandledByInterceptor();
    274 }
    275 
    276 
    277 v8::Handle<v8::Value> npObjectNamedPropertySetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
    278 {
    279     NPIdentifier identifier = getStringIdentifier(name);
    280     return npObjectSetProperty(info.Holder(), identifier, value);
    281 }
    282 
    283 
    284 v8::Handle<v8::Value> npObjectIndexedPropertySetter(uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
    285 {
    286     NPIdentifier identifier = _NPN_GetIntIdentifier(index);
    287     return npObjectSetProperty(info.Holder(), identifier, value);
    288 }
    289 
    290 v8::Handle<v8::Value> npObjectSetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name, v8::Local<v8::Value> value)
    291 {
    292     NPIdentifier identifier = getStringIdentifier(name);
    293     return npObjectSetProperty(self, identifier, value);
    294 }
    295 
    296 v8::Handle<v8::Value> npObjectSetIndexedProperty(v8::Local<v8::Object> self, uint32_t index, v8::Local<v8::Value> value)
    297 {
    298     NPIdentifier identifier = _NPN_GetIntIdentifier(index);
    299     return npObjectSetProperty(self, identifier, value);
    300 }
    301 
    302 v8::Handle<v8::Array> npObjectPropertyEnumerator(const v8::AccessorInfo& info, bool namedProperty)
    303 {
    304     NPObject* npObject = v8ObjectToNPObject(info.Holder());
    305 
    306     // Verify that our wrapper wasn't using a NPObject which
    307     // has already been deleted.
    308     if (!npObject || !_NPN_IsAlive(npObject))
    309         throwError("NPObject deleted", V8Proxy::ReferenceError);
    310 
    311     if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate) {
    312         uint32_t count;
    313         NPIdentifier* identifiers;
    314         if (npObject->_class->enumerate(npObject, &identifiers, &count)) {
    315             v8::Handle<v8::Array> properties = v8::Array::New(count);
    316             for (uint32_t i = 0; i < count; ++i) {
    317                 IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]);
    318                 if (namedProperty)
    319                     properties->Set(v8::Integer::New(i), v8::String::New(identifier->string()));
    320                 else
    321                     properties->Set(v8::Integer::New(i), v8::Integer::New(identifier->number()));
    322             }
    323 
    324             return properties;
    325         }
    326     }
    327 
    328     return v8::Handle<v8::Array>();
    329 }
    330 
    331 v8::Handle<v8::Array> npObjectNamedPropertyEnumerator(const v8::AccessorInfo& info)
    332 {
    333     return npObjectPropertyEnumerator(info, true);
    334 }
    335 
    336 v8::Handle<v8::Array> npObjectIndexedPropertyEnumerator(const v8::AccessorInfo& info)
    337 {
    338     return npObjectPropertyEnumerator(info, false);
    339 }
    340 
    341 static void weakNPObjectCallback(v8::Persistent<v8::Value>, void* parameter);
    342 
    343 static DOMWrapperMap<NPObject> staticNPObjectMap(&weakNPObjectCallback);
    344 
    345 static void weakNPObjectCallback(v8::Persistent<v8::Value> object, void* parameter)
    346 {
    347     NPObject* npObject = static_cast<NPObject*>(parameter);
    348     ASSERT(staticNPObjectMap.contains(npObject));
    349     ASSERT(npObject);
    350 
    351     // Must remove from our map before calling _NPN_ReleaseObject(). _NPN_ReleaseObject can call ForgetV8ObjectForNPObject, which
    352     // uses the table as well.
    353     staticNPObjectMap.forget(npObject);
    354 
    355     if (_NPN_IsAlive(npObject))
    356         _NPN_ReleaseObject(npObject);
    357 }
    358 
    359 
    360 v8::Local<v8::Object> createV8ObjectForNPObject(NPObject* object, NPObject* root)
    361 {
    362     static v8::Persistent<v8::FunctionTemplate> npObjectDesc;
    363 
    364     ASSERT(v8::Context::InContext());
    365 
    366     // If this is a v8 object, just return it.
    367     if (object->_class == npScriptObjectClass) {
    368         V8NPObject* v8NPObject = reinterpret_cast<V8NPObject*>(object);
    369         return v8::Local<v8::Object>::New(v8NPObject->v8Object);
    370     }
    371 
    372     // If we've already wrapped this object, just return it.
    373     if (staticNPObjectMap.contains(object))
    374         return v8::Local<v8::Object>::New(staticNPObjectMap.get(object));
    375 
    376     // FIXME: we should create a Wrapper type as a subclass of JSObject. It has two internal fields, field 0 is the wrapped
    377     // pointer, and field 1 is the type. There should be an api function that returns unused type id. The same Wrapper type
    378     // can be used by DOM bindings.
    379     if (npObjectDesc.IsEmpty()) {
    380         npObjectDesc = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
    381         npObjectDesc->InstanceTemplate()->SetInternalFieldCount(npObjectInternalFieldCount);
    382         npObjectDesc->InstanceTemplate()->SetNamedPropertyHandler(npObjectNamedPropertyGetter, npObjectNamedPropertySetter, npObjectQueryProperty, 0, npObjectNamedPropertyEnumerator);
    383         npObjectDesc->InstanceTemplate()->SetIndexedPropertyHandler(npObjectIndexedPropertyGetter, npObjectIndexedPropertySetter, 0, 0, npObjectIndexedPropertyEnumerator);
    384         npObjectDesc->InstanceTemplate()->SetCallAsFunctionHandler(npObjectInvokeDefaultHandler);
    385     }
    386 
    387     v8::Handle<v8::Function> v8Function = npObjectDesc->GetFunction();
    388     v8::Local<v8::Object> value = SafeAllocation::newInstance(v8Function);
    389 
    390     // If we were unable to allocate the instance, we avoid wrapping and registering the NP object.
    391     if (value.IsEmpty())
    392         return value;
    393 
    394     V8DOMWrapper::setDOMWrapper(value, npObjectTypeInfo(), object);
    395 
    396     // KJS retains the object as part of its wrapper (see Bindings::CInstance).
    397     _NPN_RetainObject(object);
    398 
    399     _NPN_RegisterObject(object, root);
    400 
    401     // Maintain a weak pointer for v8 so we can cleanup the object.
    402     v8::Persistent<v8::Object> weakRef = v8::Persistent<v8::Object>::New(value);
    403     staticNPObjectMap.set(object, weakRef);
    404 
    405     return value;
    406 }
    407 
    408 void forgetV8ObjectForNPObject(NPObject* object)
    409 {
    410     if (staticNPObjectMap.contains(object)) {
    411         v8::HandleScope scope;
    412         v8::Persistent<v8::Object> handle(staticNPObjectMap.get(object));
    413         V8DOMWrapper::setDOMWrapper(handle, npObjectTypeInfo(), 0);
    414         staticNPObjectMap.forget(object);
    415         _NPN_ReleaseObject(object);
    416     }
    417 }
    418 
    419 } // namespace WebCore
    420