diff options
Diffstat (limited to 'V8Binding/bindings/npruntime.cpp')
-rw-r--r-- | V8Binding/bindings/npruntime.cpp | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/V8Binding/bindings/npruntime.cpp b/V8Binding/bindings/npruntime.cpp new file mode 100644 index 0000000..f32c63d --- /dev/null +++ b/V8Binding/bindings/npruntime.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007-2009 Google, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "NPV8Object.h" +#include "npruntime_priv.h" +#include "V8NPObject.h" + +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/Assertions.h> + +// FIXME: Consider removing locks if we're singlethreaded already. +// The static initializer here should work okay, but we want to avoid +// static initialization in general. +// +// Commenting out the locks to avoid dependencies on chrome for now. +// Need a platform abstraction which we can use. +// static Lock StringIdentifierMapLock; + +namespace { + +// We use StringKey here as the key-type to avoid a string copy to +// construct the map key and for faster comparisons than strcmp. +class StringKey { + public: + explicit StringKey(const char* str) + : m_string(str), m_length(strlen(str)) {} + StringKey() : m_string(0), m_length(0) {} + explicit StringKey(WTF::HashTableDeletedValueType) + : m_string(hashTableDeletedValue()), m_length(0) { } + + StringKey& operator=(const StringKey& other) { + this->m_string = other.m_string; + this->m_length = other.m_length; + return *this; + } + + bool isHashTableDeletedValue() const { + return m_string == hashTableDeletedValue(); + } + + const char* m_string; + size_t m_length; + private: + const char* hashTableDeletedValue() const { + return reinterpret_cast<const char*>(-1); + } +}; + +inline bool operator==(const StringKey& x, const StringKey& y) { + if (x.m_length != y.m_length) { + return false; + } else if (x.m_string == y.m_string) { + return true; + } else { + ASSERT(!x.isHashTableDeletedValue() && !y.isHashTableDeletedValue()); + return memcmp(x.m_string, y.m_string, y.m_length) == 0; + } +} + +// Implement WTF::DefaultHash<StringKey>::Hash interface. +struct StringKeyHash { + static unsigned hash(const StringKey& key) { + // Compute string hash. + unsigned hash = 0; + size_t len = key.m_length; + const char* str = key.m_string; + for (size_t i = 0; i < len; i++) { + char c = str[i]; + hash += c; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + if (hash == 0) { + hash = 27; + } + return hash; + } + + static bool equal(const StringKey& x, const StringKey& y) { + return x == y; + } + + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +} // namespace + +// Implement HashTraits<StringKey> +struct StringKeyHashTraits : WTF::GenericHashTraits<StringKey> { + static void constructDeletedValue(StringKey& slot) { + new (&slot) StringKey(WTF::HashTableDeletedValue); + } + static bool isDeletedValue(const StringKey& value) { + return value.isHashTableDeletedValue(); + } +}; + +typedef WTF::HashMap<StringKey, PrivateIdentifier*, \ + StringKeyHash, StringKeyHashTraits> StringIdentifierMap; + +static StringIdentifierMap* getStringIdentifierMap() { + static StringIdentifierMap* stringIdentifierMap = 0; + if (!stringIdentifierMap) + stringIdentifierMap = new StringIdentifierMap(); + return stringIdentifierMap; +} + +// FIXME: Consider removing locks if we're singlethreaded already. +// static Lock IntIdentifierMapLock; + +typedef WTF::HashMap<int, PrivateIdentifier*> IntIdentifierMap; + +static IntIdentifierMap* getIntIdentifierMap() { + static IntIdentifierMap* intIdentifierMap = 0; + if (!intIdentifierMap) + intIdentifierMap = new IntIdentifierMap(); + return intIdentifierMap; +} + +extern "C" { + +NPIdentifier NPN_GetStringIdentifier(const NPUTF8* name) { + ASSERT(name); + + if (name) { + // AutoLock safeLock(StringIdentifierMapLock); + + StringKey key(name); + StringIdentifierMap* identMap = getStringIdentifierMap(); + StringIdentifierMap::iterator iter = identMap->find(key); + if (iter != identMap->end()) + return static_cast<NPIdentifier>(iter->second); + + size_t nameLen = key.m_length; + + // We never release identifiers, so this dictionary will grow. + PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>( + malloc(sizeof(PrivateIdentifier) + nameLen + 1)); + char* nameStorage = reinterpret_cast<char*>(identifier + 1); + memcpy(nameStorage, name, nameLen + 1); + identifier->isString = true; + identifier->value.string = reinterpret_cast<NPUTF8*>(nameStorage); + key.m_string = nameStorage; + identMap->set(key, identifier); + return (NPIdentifier)identifier; + } + + return 0; +} + +void NPN_GetStringIdentifiers(const NPUTF8** names, int32_t nameCount, + NPIdentifier* identifiers) { + ASSERT(names); + ASSERT(identifiers); + + if (names && identifiers) + for (int i = 0; i < nameCount; i++) + identifiers[i] = NPN_GetStringIdentifier(names[i]); +} + +NPIdentifier NPN_GetIntIdentifier(int32_t intid) { + // AutoLock safeLock(IntIdentifierMapLock); + // Special case for -1 and 0, both cannot be used as key in HashMap. + if (intid == 0 || intid == -1) { + static PrivateIdentifier* minusOneOrZeroIds[2]; + PrivateIdentifier* id = minusOneOrZeroIds[intid + 1]; + if (!id) { + id = reinterpret_cast<PrivateIdentifier*>( + malloc(sizeof(PrivateIdentifier))); + id->isString = false; + id->value.number = intid; + minusOneOrZeroIds[intid + 1] = id; + } + return (NPIdentifier)id; + } + + IntIdentifierMap* identMap = getIntIdentifierMap(); + IntIdentifierMap::iterator iter = identMap->find(intid); + if (iter != identMap->end()) + return static_cast<NPIdentifier>(iter->second); + + // We never release identifiers, so this dictionary will grow. + PrivateIdentifier* identifier = reinterpret_cast<PrivateIdentifier*>( + malloc(sizeof(PrivateIdentifier))); + identifier->isString = false; + identifier->value.number = intid; + identMap->set(intid, identifier); + return (NPIdentifier)identifier; +} + +bool NPN_IdentifierIsString(NPIdentifier identifier) { + PrivateIdentifier* i = reinterpret_cast<PrivateIdentifier*>(identifier); + return i->isString; +} + +NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier) { + PrivateIdentifier* i = reinterpret_cast<PrivateIdentifier*>(identifier); + if (!i->isString || !i->value.string) + return NULL; + + return (NPUTF8 *)strdup(i->value.string); +} + +int32_t NPN_IntFromIdentifier(NPIdentifier identifier) { + PrivateIdentifier* i = reinterpret_cast<PrivateIdentifier*>(identifier); + if (i->isString) + return 0; + return i->value.number; +} + +void NPN_ReleaseVariantValue(NPVariant* variant) { + ASSERT(variant); + + if (variant->type == NPVariantType_Object) { + NPN_ReleaseObject(variant->value.objectValue); + variant->value.objectValue = 0; + } else if (variant->type == NPVariantType_String) { + free((void*)variant->value.stringValue.UTF8Characters); + variant->value.stringValue.UTF8Characters = 0; + variant->value.stringValue.UTF8Length = 0; + } + + variant->type = NPVariantType_Void; +} + +NPObject *NPN_CreateObject(NPP npp, NPClass* aClass) { + ASSERT(aClass); + + if (aClass) { + NPObject* obj; + if (aClass->allocate != NULL) + obj = aClass->allocate(npp, aClass); + else + obj = reinterpret_cast<NPObject*>(malloc(sizeof(NPObject))); + + obj->_class = aClass; + obj->referenceCount = 1; + return obj; + } + + return 0; +} + +NPObject* NPN_RetainObject(NPObject* obj) { + ASSERT(obj); + ASSERT(obj->referenceCount > 0); + + if (obj) + obj->referenceCount++; + + return obj; +} + +// _NPN_DeallocateObject actually deletes the object. Technically, +// callers should use NPN_ReleaseObject. Webkit exposes this function +// to kill objects which plugins may not have properly released. +void _NPN_DeallocateObject(NPObject *obj) { + ASSERT(obj); + ASSERT(obj->referenceCount >= 0); + + if (obj) { + // NPObjects that remain in pure C++ may never have wrappers. + // Hence, if it's not already alive, don't unregister it. + // If it is alive, unregister it as the *last* thing we do + // so that it can do as much cleanup as possible on its own. + if (_NPN_IsAlive(obj)) + _NPN_UnregisterObject(obj); + + obj->referenceCount = -1; + if (obj->_class->deallocate) + obj->_class->deallocate(obj); + else + free(obj); + } +} + +void NPN_ReleaseObject(NPObject* obj) { + ASSERT(obj); + ASSERT(obj->referenceCount >= 1); + + if (obj && obj->referenceCount >= 1) { + if (--obj->referenceCount == 0) + _NPN_DeallocateObject(obj); + } +} + +void _NPN_InitializeVariantWithStringCopy(NPVariant* variant, + const NPString* value) { + variant->type = NPVariantType_String; + variant->value.stringValue.UTF8Length = value->UTF8Length; + variant->value.stringValue.UTF8Characters = + reinterpret_cast<NPUTF8*>(malloc(sizeof(NPUTF8) * value->UTF8Length)); + memcpy((void*)variant->value.stringValue.UTF8Characters, + value->UTF8Characters, + sizeof(NPUTF8) * value->UTF8Length); +} + + +// NPN_Registry +// +// The registry is designed for quick lookup of NPObjects. +// JS needs to be able to quickly lookup a given NPObject to determine +// if it is alive or not. +// The browser needs to be able to quickly lookup all NPObjects which are +// "owned" by an object. +// +// The g_live_objects is a hash table of all live objects to their owner +// objects. Presence in this table is used primarily to determine if +// objects are live or not. +// +// The g_root_objects is a hash table of root objects to a set of +// objects that should be deactivated in sync with the root. A +// root is defined as a top-level owner object. This is used on +// Frame teardown to deactivate all objects associated +// with a particular plugin. + +typedef WTF::HashSet<NPObject*> NPObjectSet; +typedef WTF::HashMap<NPObject*, NPObject*> NPObjectMap; +typedef WTF::HashMap<NPObject*, NPObjectSet*> NPRootObjectMap; + +// A map of live NPObjects with pointers to their Roots. +NPObjectMap g_live_objects; + +// A map of the root objects and the list of NPObjects +// associated with that object. +NPRootObjectMap g_root_objects; + +void _NPN_RegisterObject(NPObject* obj, NPObject* owner) { + ASSERT(obj); + + // Check if already registered. + if (g_live_objects.find(obj) != g_live_objects.end()) { + return; + } + + if (!owner) { + // Registering a new owner object. + ASSERT(g_root_objects.find(obj) == g_root_objects.end()); + g_root_objects.set(obj, new NPObjectSet()); + } else { + // Always associate this object with it's top-most parent. + // Since we always flatten, we only have to look up one level. + NPObjectMap::iterator owner_entry = g_live_objects.find(owner); + NPObject* parent = NULL; + if (g_live_objects.end() != owner_entry) + parent = owner_entry->second; + + if (parent) { + owner = parent; + } + ASSERT(g_root_objects.find(obj) == g_root_objects.end()); + if (g_root_objects.find(owner) != g_root_objects.end()) + g_root_objects.get(owner)->add(obj); + } + + ASSERT(g_live_objects.find(obj) == g_live_objects.end()); + g_live_objects.set(obj, owner); +} + +void _NPN_UnregisterObject(NPObject* obj) { + ASSERT(obj); + ASSERT(g_live_objects.find(obj) != g_live_objects.end()); + + NPObject* owner = NULL; + if (g_live_objects.find(obj) != g_live_objects.end()) + owner = g_live_objects.find(obj)->second; + + if (owner == NULL) { + // Unregistering a owner object; also unregister it's descendants. + ASSERT(g_root_objects.find(obj) != g_root_objects.end()); + NPObjectSet* set = g_root_objects.get(obj); + while (set->size() > 0) { +#ifndef NDEBUG + int size = set->size(); +#endif + NPObject* sub_object = *(set->begin()); + // The sub-object should not be a owner! + ASSERT(g_root_objects.find(sub_object) == g_root_objects.end()); + + // First, unregister the object. + set->remove(sub_object); + g_live_objects.remove(sub_object); + + // Remove the JS references to the object. + ForgetV8ObjectForNPObject(sub_object); + + ASSERT(set->size() < size); + } + delete set; + g_root_objects.remove(obj); + } else { + NPRootObjectMap::iterator owner_entry = g_root_objects.find(owner); + if (owner_entry != g_root_objects.end()) { + NPObjectSet* list = owner_entry->second; + ASSERT(list->find(obj) != list->end()); + list->remove(obj); + } + } + g_live_objects.remove(obj); + ForgetV8ObjectForNPObject(obj); +} + +bool _NPN_IsAlive(NPObject* obj) { + return g_live_objects.find(obj) != g_live_objects.end(); +} + +} // extern "C" |