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