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