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 "bindings/v8/V8NPObject.h"
     34 
     35 #include "bindings/core/v8/V8HTMLAppletElement.h"
     36 #include "bindings/core/v8/V8HTMLEmbedElement.h"
     37 #include "bindings/core/v8/V8HTMLObjectElement.h"
     38 #include "bindings/v8/NPV8Object.h"
     39 #include "bindings/v8/V8Binding.h"
     40 #include "bindings/v8/V8NPUtils.h"
     41 #include "bindings/v8/V8ObjectConstructor.h"
     42 #include "bindings/v8/V8PersistentValueMap.h"
     43 #include "bindings/v8/npruntime_impl.h"
     44 #include "bindings/v8/npruntime_priv.h"
     45 #include "core/html/HTMLPlugInElement.h"
     46 #include "v8-util.h"
     47 #include "wtf/OwnPtr.h"
     48 
     49 namespace WebCore {
     50 
     51 enum InvokeFunctionType {
     52     InvokeMethod = 1,
     53     InvokeConstruct = 2,
     54     InvokeDefault = 3
     55 };
     56 
     57 struct IdentifierRep {
     58     int number() const { return m_isString ? 0 : m_value.m_number; }
     59     const char* string() const { return m_isString ? m_value.m_string : 0; }
     60 
     61     union {
     62         const char* m_string;
     63         int m_number;
     64     } m_value;
     65     bool m_isString;
     66 };
     67 
     68 // FIXME: need comments.
     69 // Params: holder could be HTMLEmbedElement or NPObject
     70 static void npObjectInvokeImpl(const v8::FunctionCallbackInfo<v8::Value>& info, InvokeFunctionType functionId)
     71 {
     72     NPObject* npObject;
     73     v8::Isolate* isolate = info.GetIsolate();
     74 
     75     // These three types are subtypes of HTMLPlugInElement.
     76     HTMLPlugInElement* element = V8HTMLAppletElement::toNativeWithTypeCheck(isolate, info.Holder());
     77     if (!element) {
     78         element = V8HTMLEmbedElement::toNativeWithTypeCheck(isolate, info.Holder());
     79         if (!element) {
     80             element = V8HTMLObjectElement::toNativeWithTypeCheck(isolate, info.Holder());
     81         }
     82     }
     83     if (element) {
     84         if (RefPtr<SharedPersistent<v8::Object> > wrapper = element->pluginWrapper()) {
     85             v8::HandleScope handleScope(isolate);
     86             npObject = v8ObjectToNPObject(wrapper->newLocal(isolate));
     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 (info.Holder()->InternalFieldCount() != npObjectInternalFieldCount) {
     93             throwError(v8ReferenceError, "NPMethod called on non-NPObject", info.GetIsolate());
     94             return;
     95         }
     96 
     97         npObject = v8ObjectToNPObject(info.Holder());
     98     }
     99 
    100     // Verify that our wrapper wasn't using a NPObject which has already been deleted.
    101     if (!npObject || !_NPN_IsAlive(npObject)) {
    102         throwError(v8ReferenceError, "NPObject deleted", isolate);
    103         return;
    104     }
    105 
    106     // Wrap up parameters.
    107     int numArgs = info.Length();
    108     OwnPtr<NPVariant[]> npArgs = adoptArrayPtr(new NPVariant[numArgs]);
    109 
    110     for (int i = 0; i < numArgs; i++)
    111         convertV8ObjectToNPVariant(info[i], npObject, &npArgs[i], isolate);
    112 
    113     NPVariant result;
    114     VOID_TO_NPVARIANT(result);
    115 
    116     bool retval = true;
    117     switch (functionId) {
    118     case InvokeMethod:
    119         if (npObject->_class->invoke) {
    120             v8::Handle<v8::String> functionName = v8::Handle<v8::String>::Cast(info.Data());
    121             NPIdentifier identifier = getStringIdentifier(functionName);
    122             retval = npObject->_class->invoke(npObject, identifier, npArgs.get(), numArgs, &result);
    123         }
    124         break;
    125     case InvokeConstruct:
    126         if (npObject->_class->construct)
    127             retval = npObject->_class->construct(npObject, npArgs.get(), numArgs, &result);
    128         break;
    129     case InvokeDefault:
    130         if (npObject->_class->invokeDefault)
    131             retval = npObject->_class->invokeDefault(npObject, npArgs.get(), numArgs, &result);
    132         break;
    133     default:
    134         break;
    135     }
    136 
    137     if (!retval)
    138         throwError(v8GeneralError, "Error calling method on NPObject.", isolate);
    139 
    140     for (int i = 0; i < numArgs; i++)
    141         _NPN_ReleaseVariantValue(&npArgs[i]);
    142 
    143     // Unwrap return values.
    144     v8::Handle<v8::Value> returnValue;
    145     if (_NPN_IsAlive(npObject))
    146         returnValue = convertNPVariantToV8Object(&result, npObject, isolate);
    147     _NPN_ReleaseVariantValue(&result);
    148 
    149     v8SetReturnValue(info, returnValue);
    150 }
    151 
    152 
    153 void npObjectMethodHandler(const v8::FunctionCallbackInfo<v8::Value>& info)
    154 {
    155     return npObjectInvokeImpl(info, InvokeMethod);
    156 }
    157 
    158 
    159 void npObjectInvokeDefaultHandler(const v8::FunctionCallbackInfo<v8::Value>& info)
    160 {
    161     if (info.IsConstructCall()) {
    162         npObjectInvokeImpl(info, InvokeConstruct);
    163         return;
    164     }
    165 
    166     npObjectInvokeImpl(info, InvokeDefault);
    167 }
    168 
    169 class V8TemplateMapTraits : public V8PersistentValueMapTraits<PrivateIdentifier*, v8::FunctionTemplate, true> {
    170 public:
    171     typedef v8::PersistentValueMap<PrivateIdentifier*, v8::FunctionTemplate, V8TemplateMapTraits> MapType;
    172     typedef PrivateIdentifier WeakCallbackDataType;
    173 
    174     static WeakCallbackDataType* WeakCallbackParameter(MapType* map, PrivateIdentifier* key, const v8::Local<v8::FunctionTemplate>& value)
    175     {
    176         return key;
    177     }
    178 
    179     static void DisposeCallbackData(WeakCallbackDataType* callbackData) { }
    180 
    181     static MapType* MapFromWeakCallbackData(
    182         const v8::WeakCallbackData<v8::FunctionTemplate, WeakCallbackDataType>&);
    183 
    184     static PrivateIdentifier* KeyFromWeakCallbackData(
    185         const v8::WeakCallbackData<v8::FunctionTemplate, WeakCallbackDataType>& data)
    186     {
    187         return data.GetParameter();
    188     }
    189 
    190     // Dispose traits:
    191     static void Dispose(v8::Isolate* isolate, v8::UniquePersistent<v8::FunctionTemplate> value, PrivateIdentifier* key) { }
    192 };
    193 
    194 
    195 class V8NPTemplateMap {
    196 public:
    197     // NPIdentifier is PrivateIdentifier*.
    198     typedef v8::PersistentValueMap<PrivateIdentifier*, v8::FunctionTemplate, V8TemplateMapTraits> MapType;
    199 
    200     v8::Local<v8::FunctionTemplate> get(PrivateIdentifier* key)
    201     {
    202         return m_map.Get(key);
    203     }
    204 
    205     void set(PrivateIdentifier* key, v8::Handle<v8::FunctionTemplate> handle)
    206     {
    207         ASSERT(!m_map.Contains(key));
    208         m_map.Set(key, handle);
    209     }
    210 
    211     static V8NPTemplateMap& sharedInstance(v8::Isolate* isolate)
    212     {
    213         DEFINE_STATIC_LOCAL(V8NPTemplateMap, map, (isolate));
    214         ASSERT(isolate == map.m_map.GetIsolate());
    215         return map;
    216     }
    217 
    218     friend class V8TemplateMapTraits;
    219 
    220 private:
    221     explicit V8NPTemplateMap(v8::Isolate* isolate)
    222         : m_map(isolate)
    223     {
    224     }
    225 
    226     MapType m_map;
    227 };
    228 
    229 V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackData(const v8::WeakCallbackData<v8::FunctionTemplate, WeakCallbackDataType>& data)
    230 {
    231     return &V8NPTemplateMap::sharedInstance(data.GetIsolate()).m_map;
    232 }
    233 
    234 
    235 static v8::Handle<v8::Value> npObjectGetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> key, v8::Isolate* isolate)
    236 {
    237     NPObject* npObject = v8ObjectToNPObject(self);
    238 
    239     // Verify that our wrapper wasn't using a NPObject which
    240     // has already been deleted.
    241     if (!npObject || !_NPN_IsAlive(npObject))
    242         return throwError(v8ReferenceError, "NPObject deleted", isolate);
    243 
    244 
    245     if (npObject->_class->hasProperty && npObject->_class->getProperty && npObject->_class->hasProperty(npObject, identifier)) {
    246         if (!_NPN_IsAlive(npObject))
    247             return throwError(v8ReferenceError, "NPObject deleted", isolate);
    248 
    249         NPVariant result;
    250         VOID_TO_NPVARIANT(result);
    251         if (!npObject->_class->getProperty(npObject, identifier, &result))
    252             return v8Undefined();
    253 
    254         v8::Handle<v8::Value> returnValue;
    255         if (_NPN_IsAlive(npObject))
    256             returnValue = convertNPVariantToV8Object(&result, npObject, isolate);
    257         _NPN_ReleaseVariantValue(&result);
    258         return returnValue;
    259 
    260     }
    261 
    262     if (!_NPN_IsAlive(npObject))
    263         return throwError(v8ReferenceError, "NPObject deleted", isolate);
    264 
    265     if (key->IsString() && npObject->_class->hasMethod && npObject->_class->hasMethod(npObject, identifier)) {
    266         if (!_NPN_IsAlive(npObject))
    267             return throwError(v8ReferenceError, "NPObject deleted", isolate);
    268 
    269         PrivateIdentifier* id = static_cast<PrivateIdentifier*>(identifier);
    270         v8::Local<v8::FunctionTemplate> functionTemplate = V8NPTemplateMap::sharedInstance(isolate).get(id);
    271         // Cache templates using identifier as the key.
    272         if (functionTemplate.IsEmpty()) {
    273             // Create a new template.
    274             functionTemplate = v8::FunctionTemplate::New(isolate);
    275             functionTemplate->SetCallHandler(npObjectMethodHandler, key);
    276             V8NPTemplateMap::sharedInstance(isolate).set(id, functionTemplate);
    277         }
    278         v8::Local<v8::Function> v8Function = functionTemplate->GetFunction();
    279         v8Function->SetName(v8::Handle<v8::String>::Cast(key));
    280         return v8Function;
    281     }
    282 
    283     return v8Undefined();
    284 }
    285 
    286 void npObjectNamedPropertyGetter(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)
    287 {
    288     NPIdentifier identifier = getStringIdentifier(name);
    289     v8SetReturnValue(info, npObjectGetProperty(info.Holder(), identifier, name, info.GetIsolate()));
    290 }
    291 
    292 void npObjectIndexedPropertyGetter(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info)
    293 {
    294     NPIdentifier identifier = _NPN_GetIntIdentifier(index);
    295     v8SetReturnValue(info, npObjectGetProperty(info.Holder(), identifier, v8::Number::New(info.GetIsolate(), index), info.GetIsolate()));
    296 }
    297 
    298 void npObjectGetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)
    299 {
    300     NPIdentifier identifier = getStringIdentifier(name);
    301     v8SetReturnValue(info, npObjectGetProperty(self, identifier, name, info.GetIsolate()));
    302 }
    303 
    304 void npObjectGetIndexedProperty(v8::Local<v8::Object> self, uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info)
    305 {
    306     NPIdentifier identifier = _NPN_GetIntIdentifier(index);
    307     v8SetReturnValue(info, npObjectGetProperty(self, identifier, v8::Number::New(info.GetIsolate(), index), info.GetIsolate()));
    308 }
    309 
    310 void npObjectQueryProperty(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Integer>& info)
    311 {
    312     NPIdentifier identifier = getStringIdentifier(name);
    313     if (npObjectGetProperty(info.Holder(), identifier, name, info.GetIsolate()).IsEmpty())
    314         return;
    315     v8SetReturnValueInt(info, 0);
    316 }
    317 
    318 static v8::Handle<v8::Value> npObjectSetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> value, v8::Isolate* isolate)
    319 {
    320     NPObject* npObject = v8ObjectToNPObject(self);
    321 
    322     // Verify that our wrapper wasn't using a NPObject which has already been deleted.
    323     if (!npObject || !_NPN_IsAlive(npObject)) {
    324         throwError(v8ReferenceError, "NPObject deleted", isolate);
    325         return value;  // Intercepted, but an exception was thrown.
    326     }
    327 
    328     if (npObject->_class->hasProperty && npObject->_class->setProperty && npObject->_class->hasProperty(npObject, identifier)) {
    329         if (!_NPN_IsAlive(npObject))
    330             return throwError(v8ReferenceError, "NPObject deleted", isolate);
    331 
    332         NPVariant npValue;
    333         VOID_TO_NPVARIANT(npValue);
    334         convertV8ObjectToNPVariant(value, npObject, &npValue, isolate);
    335         bool success = npObject->_class->setProperty(npObject, identifier, &npValue);
    336         _NPN_ReleaseVariantValue(&npValue);
    337         if (success)
    338             return value; // Intercept the call.
    339     }
    340     return v8Undefined();
    341 }
    342 
    343 
    344 void npObjectNamedPropertySetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info)
    345 {
    346     NPIdentifier identifier = getStringIdentifier(name);
    347     v8SetReturnValue(info, npObjectSetProperty(info.Holder(), identifier, value, info.GetIsolate()));
    348 }
    349 
    350 
    351 void npObjectIndexedPropertySetter(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info)
    352 {
    353     NPIdentifier identifier = _NPN_GetIntIdentifier(index);
    354     v8SetReturnValue(info, npObjectSetProperty(info.Holder(), identifier, value, info.GetIsolate()));
    355 }
    356 
    357 void npObjectSetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info)
    358 {
    359     NPIdentifier identifier = getStringIdentifier(name);
    360     v8SetReturnValue(info, npObjectSetProperty(self, identifier, value, info.GetIsolate()));
    361 }
    362 
    363 void npObjectSetIndexedProperty(v8::Local<v8::Object> self, uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info)
    364 {
    365     NPIdentifier identifier = _NPN_GetIntIdentifier(index);
    366     v8SetReturnValue(info, npObjectSetProperty(self, identifier, value, info.GetIsolate()));
    367 }
    368 
    369 void npObjectPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info, bool namedProperty)
    370 {
    371     NPObject* npObject = v8ObjectToNPObject(info.Holder());
    372 
    373     // Verify that our wrapper wasn't using a NPObject which
    374     // has already been deleted.
    375     if (!npObject || !_NPN_IsAlive(npObject))
    376         throwError(v8ReferenceError, "NPObject deleted", info.GetIsolate());
    377 
    378     if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate) {
    379         uint32_t count;
    380         NPIdentifier* identifiers;
    381         if (npObject->_class->enumerate(npObject, &identifiers, &count)) {
    382             uint32_t propertiesCount = 0;
    383             for (uint32_t i = 0; i < count; ++i) {
    384                 IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]);
    385                 if (namedProperty == identifier->m_isString)
    386                     ++propertiesCount;
    387             }
    388             v8::Handle<v8::Array> properties = v8::Array::New(info.GetIsolate(), propertiesCount);
    389             for (uint32_t i = 0, propertyIndex = 0; i < count; ++i) {
    390                 IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]);
    391                 if (namedProperty == identifier->m_isString) {
    392                     ASSERT(propertyIndex < propertiesCount);
    393                     if (namedProperty)
    394                         properties->Set(v8::Integer::New(info.GetIsolate(), propertyIndex++), v8AtomicString(info.GetIsolate(), identifier->string()));
    395                     else
    396                         properties->Set(v8::Integer::New(info.GetIsolate(), propertyIndex++), v8::Integer::New(info.GetIsolate(), identifier->number()));
    397                 }
    398             }
    399 
    400             v8SetReturnValue(info, properties);
    401             return;
    402         }
    403     }
    404 }
    405 
    406 void npObjectNamedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info)
    407 {
    408     npObjectPropertyEnumerator(info, true);
    409 }
    410 
    411 void npObjectIndexedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info)
    412 {
    413     npObjectPropertyEnumerator(info, false);
    414 }
    415 
    416 static DOMWrapperMap<NPObject>& staticNPObjectMap()
    417 {
    418     DEFINE_STATIC_LOCAL(DOMWrapperMap<NPObject>, npObjectMap, (v8::Isolate::GetCurrent()));
    419     return npObjectMap;
    420 }
    421 
    422 template <>
    423 inline void DOMWrapperMap<NPObject>::PersistentValueMapTraits::Dispose(
    424     v8::Isolate* isolate,
    425     v8::UniquePersistent<v8::Object> value,
    426     NPObject* npObject)
    427 {
    428     ASSERT(npObject);
    429     if (_NPN_IsAlive(npObject))
    430         _NPN_ReleaseObject(npObject);
    431 }
    432 
    433 v8::Local<v8::Object> createV8ObjectForNPObject(NPObject* object, NPObject* root, v8::Isolate* isolate)
    434 {
    435     static v8::Eternal<v8::FunctionTemplate> npObjectDesc;
    436 
    437     ASSERT(isolate->InContext());
    438 
    439     // If this is a v8 object, just return it.
    440     V8NPObject* v8NPObject = npObjectToV8NPObject(object);
    441     if (v8NPObject)
    442         return v8::Local<v8::Object>::New(isolate, v8NPObject->v8Object);
    443 
    444     // If we've already wrapped this object, just return it.
    445     v8::Handle<v8::Object> wrapper = staticNPObjectMap().newLocal(object, isolate);
    446     if (!wrapper.IsEmpty())
    447         return wrapper;
    448 
    449     // FIXME: we should create a Wrapper type as a subclass of JSObject. It has two internal fields, field 0 is the wrapped
    450     // pointer, and field 1 is the type. There should be an api function that returns unused type id. The same Wrapper type
    451     // can be used by DOM bindings.
    452     if (npObjectDesc.IsEmpty()) {
    453         v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
    454         templ->InstanceTemplate()->SetInternalFieldCount(npObjectInternalFieldCount);
    455         templ->InstanceTemplate()->SetNamedPropertyHandler(npObjectNamedPropertyGetter, npObjectNamedPropertySetter, npObjectQueryProperty, 0, npObjectNamedPropertyEnumerator);
    456         templ->InstanceTemplate()->SetIndexedPropertyHandler(npObjectIndexedPropertyGetter, npObjectIndexedPropertySetter, 0, 0, npObjectIndexedPropertyEnumerator);
    457         templ->InstanceTemplate()->SetCallAsFunctionHandler(npObjectInvokeDefaultHandler);
    458         npObjectDesc.Set(isolate, templ);
    459     }
    460 
    461     // FIXME: Move staticNPObjectMap() to DOMDataStore.
    462     // Use V8DOMWrapper::createWrapper() and
    463     // V8DOMWrapper::associateObjectWithWrapper()
    464     // to create a wrapper object.
    465     v8::Handle<v8::Function> v8Function = npObjectDesc.Get(isolate)->GetFunction();
    466     v8::Local<v8::Object> value = V8ObjectConstructor::newInstance(isolate, v8Function);
    467     if (value.IsEmpty())
    468         return value;
    469 
    470     V8DOMWrapper::setNativeInfo(value, npObjectTypeInfo(), object);
    471 
    472     // KJS retains the object as part of its wrapper (see Bindings::CInstance).
    473     _NPN_RetainObject(object);
    474     _NPN_RegisterObject(object, root);
    475 
    476     WrapperConfiguration configuration = buildWrapperConfiguration(object, WrapperConfiguration::Dependent);
    477     staticNPObjectMap().set(object, value, configuration);
    478     ASSERT(V8DOMWrapper::isDOMWrapper(value));
    479     return value;
    480 }
    481 
    482 void forgetV8ObjectForNPObject(NPObject* object)
    483 {
    484     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    485     v8::HandleScope scope(isolate);
    486     v8::Handle<v8::Object> wrapper = staticNPObjectMap().newLocal(object, isolate);
    487     if (!wrapper.IsEmpty()) {
    488         V8DOMWrapper::clearNativeInfo(wrapper, npObjectTypeInfo());
    489         staticNPObjectMap().removeAndDispose(object);
    490         _NPN_ReleaseObject(object);
    491     }
    492 }
    493 
    494 } // namespace WebCore
    495