diff options
author | Ben Murdoch <benm@google.com> | 2009-08-11 17:01:47 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2009-08-11 18:21:02 +0100 |
commit | 0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5 (patch) | |
tree | 2943df35f62d885c89d01063cc528dd73b480fea /WebCore/bindings/v8/npruntime.cpp | |
parent | 7e7a70bfa49a1122b2597a1e6367d89eb4035eca (diff) | |
download | external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.zip external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.gz external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.bz2 |
Merge in WebKit r47029.
Diffstat (limited to 'WebCore/bindings/v8/npruntime.cpp')
-rw-r--r-- | WebCore/bindings/v8/npruntime.cpp | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/WebCore/bindings/v8/npruntime.cpp b/WebCore/bindings/v8/npruntime.cpp new file mode 100644 index 0000000..64a1927 --- /dev/null +++ b/WebCore/bindings/v8/npruntime.cpp @@ -0,0 +1,442 @@ +/* + * 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_impl.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. + +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; + if (x.m_string == y.m_string) + return true; + + ASSERT(!x.isHashTableDeletedValue() && !y.isHashTableDeletedValue()); + return !memcmp(x.m_string, y.m_string, y.m_length); +} + +// 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; +} + +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) { + + 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) +{ + // Special case for -1 and 0, both cannot be used as key in HashMap. + if (!intId || 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* privateIdentifier = reinterpret_cast<PrivateIdentifier*>(identifier); + return privateIdentifier->isString; +} + +NPUTF8 *_NPN_UTF8FromIdentifier(NPIdentifier identifier) +{ + PrivateIdentifier* privateIdentifier = reinterpret_cast<PrivateIdentifier*>(identifier); + if (!privateIdentifier->isString || !privateIdentifier->value.string) + return 0; + + return (NPUTF8*) strdup(privateIdentifier->value.string); +} + +int32_t _NPN_IntFromIdentifier(NPIdentifier identifier) +{ + PrivateIdentifier* privateIdentifier = reinterpret_cast<PrivateIdentifier*>(identifier); + if (privateIdentifier->isString) + return 0; + return privateIdentifier->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* npClass) +{ + ASSERT(npClass); + + if (npClass) { + NPObject* npObject; + if (npClass->allocate != 0) + npObject = npClass->allocate(npp, npClass); + else + npObject = reinterpret_cast<NPObject*>(malloc(sizeof(NPObject))); + + npObject->_class = npClass; + npObject->referenceCount = 1; + return npObject; + } + + return 0; +} + +NPObject* _NPN_RetainObject(NPObject* npObject) +{ + ASSERT(npObject); + ASSERT(npObject->referenceCount > 0); + + if (npObject) + npObject->referenceCount++; + + return npObject; +} + +// _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* npObject) +{ + ASSERT(npObject); + ASSERT(npObject->referenceCount >= 0); + + if (npObject) { + // 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(npObject)) + _NPN_UnregisterObject(npObject); + + npObject->referenceCount = -1; + if (npObject->_class->deallocate) + npObject->_class->deallocate(npObject); + else + free(npObject); + } +} + +void _NPN_ReleaseObject(NPObject* npObject) +{ + ASSERT(npObject); + ASSERT(npObject->referenceCount >= 1); + + if (npObject && npObject->referenceCount >= 1) { + if (!--npObject->referenceCount) + _NPN_DeallocateObject(npObject); + } +} + +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 liveObjectMap 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 rootObjectMap 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 liveObjectMap; + +// A map of the root objects and the list of NPObjects +// associated with that object. +NPRootObjectMap rootObjectMap; + +void _NPN_RegisterObject(NPObject* npObject, NPObject* owner) +{ + ASSERT(npObject); + + // Check if already registered. + if (liveObjectMap.find(npObject) != liveObjectMap.end()) + return; + + if (!owner) { + // Registering a new owner object. + ASSERT(rootObjectMap.find(npObject) == rootObjectMap.end()); + rootObjectMap.set(npObject, 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 ownerEntry = liveObjectMap.find(owner); + NPObject* parent = 0; + if (liveObjectMap.end() != ownerEntry) + parent = ownerEntry->second; + + if (parent) + owner = parent; + ASSERT(rootObjectMap.find(npObject) == rootObjectMap.end()); + if (rootObjectMap.find(owner) != rootObjectMap.end()) + rootObjectMap.get(owner)->add(npObject); + } + + ASSERT(liveObjectMap.find(npObject) == liveObjectMap.end()); + liveObjectMap.set(npObject, owner); +} + +void _NPN_UnregisterObject(NPObject* npObject) +{ + ASSERT(npObject); + ASSERT(liveObjectMap.find(npObject) != liveObjectMap.end()); + + NPObject* owner = 0; + if (liveObjectMap.find(npObject) != liveObjectMap.end()) + owner = liveObjectMap.find(npObject)->second; + + if (!owner) { + // Unregistering a owner object; also unregister it's descendants. + ASSERT(rootObjectMap.find(npObject) != rootObjectMap.end()); + NPObjectSet* set = rootObjectMap.get(npObject); + 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(rootObjectMap.find(sub_object) == rootObjectMap.end()); + + // First, unregister the object. + set->remove(sub_object); + liveObjectMap.remove(sub_object); + + // Remove the JS references to the object. + forgetV8ObjectForNPObject(sub_object); + + ASSERT(set->size() < size); + } + delete set; + rootObjectMap.remove(npObject); + } else { + NPRootObjectMap::iterator ownerEntry = rootObjectMap.find(owner); + if (ownerEntry != rootObjectMap.end()) { + NPObjectSet* list = ownerEntry->second; + ASSERT(list->find(npObject) != list->end()); + list->remove(npObject); + } + } + + liveObjectMap.remove(npObject); + forgetV8ObjectForNPObject(npObject); +} + +bool _NPN_IsAlive(NPObject* npObject) +{ + return liveObjectMap.find(npObject) != liveObjectMap.end(); +} + +} // extern "C" |