michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsScriptNameSpaceManager.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIComponentRegistrar.h" michael@0: #include "nsICategoryManager.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIScriptExternalNameSet.h" michael@0: #include "nsIScriptNameSpaceManager.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIInterfaceInfoManager.h" michael@0: #include "nsIInterfaceInfo.h" michael@0: #include "xptinfo.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsHashKeys.h" michael@0: #include "nsDOMClassInfo.h" michael@0: #include "nsCRT.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #define NS_INTERFACE_PREFIX "nsI" michael@0: #define NS_DOM_INTERFACE_PREFIX "nsIDOM" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: // Our extended PLDHashEntryHdr michael@0: class GlobalNameMapEntry : public PLDHashEntryHdr michael@0: { michael@0: public: michael@0: // Our hash table ops don't care about the order of these members michael@0: nsString mKey; michael@0: nsGlobalNameStruct mGlobalName; michael@0: michael@0: size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) { michael@0: // Measurement of the following members may be added later if DMD finds it michael@0: // is worthwhile: michael@0: // - mGlobalName michael@0: return mKey.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf); michael@0: } michael@0: }; michael@0: michael@0: michael@0: static PLDHashNumber michael@0: GlobalNameHashHashKey(PLDHashTable *table, const void *key) michael@0: { michael@0: const nsAString *str = static_cast(key); michael@0: return HashString(*str); michael@0: } michael@0: michael@0: static bool michael@0: GlobalNameHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry, michael@0: const void *key) michael@0: { michael@0: const GlobalNameMapEntry *e = michael@0: static_cast(entry); michael@0: const nsAString *str = static_cast(key); michael@0: michael@0: return str->Equals(e->mKey); michael@0: } michael@0: michael@0: static void michael@0: GlobalNameHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) michael@0: { michael@0: GlobalNameMapEntry *e = static_cast(entry); michael@0: michael@0: // An entry is being cleared, let the key (nsString) do its own michael@0: // cleanup. michael@0: e->mKey.~nsString(); michael@0: if (e->mGlobalName.mType == nsGlobalNameStruct::eTypeExternalClassInfo) { michael@0: nsIClassInfo* ci = GET_CLEAN_CI_PTR(e->mGlobalName.mData->mCachedClassInfo); michael@0: michael@0: // If we constructed an internal helper, we'll let the helper delete michael@0: // the nsDOMClassInfoData structure, if not we do it here. michael@0: if (!ci || e->mGlobalName.mData->u.mExternalConstructorFptr) { michael@0: delete e->mGlobalName.mData; michael@0: } michael@0: michael@0: // Release our pointer to the helper. michael@0: NS_IF_RELEASE(ci); michael@0: } michael@0: else if (e->mGlobalName.mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) { michael@0: delete e->mGlobalName.mAlias; michael@0: } michael@0: michael@0: // This will set e->mGlobalName.mType to michael@0: // nsGlobalNameStruct::eTypeNotInitialized michael@0: memset(&e->mGlobalName, 0, sizeof(nsGlobalNameStruct)); michael@0: } michael@0: michael@0: static bool michael@0: GlobalNameHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, michael@0: const void *key) michael@0: { michael@0: GlobalNameMapEntry *e = static_cast(entry); michael@0: const nsAString *keyStr = static_cast(key); michael@0: michael@0: // Initialize the key in the entry with placement new michael@0: new (&e->mKey) nsString(*keyStr); michael@0: michael@0: // This will set e->mGlobalName.mType to michael@0: // nsGlobalNameStruct::eTypeNotInitialized michael@0: memset(&e->mGlobalName, 0, sizeof(nsGlobalNameStruct)); michael@0: return true; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: nsScriptNameSpaceManager, michael@0: nsIObserver, michael@0: nsISupportsWeakReference, michael@0: nsIMemoryReporter) michael@0: michael@0: nsScriptNameSpaceManager::nsScriptNameSpaceManager() michael@0: : mIsInitialized(false) michael@0: { michael@0: MOZ_COUNT_CTOR(nsScriptNameSpaceManager); michael@0: } michael@0: michael@0: nsScriptNameSpaceManager::~nsScriptNameSpaceManager() michael@0: { michael@0: if (mIsInitialized) { michael@0: UnregisterWeakMemoryReporter(this); michael@0: // Destroy the hash michael@0: PL_DHashTableFinish(&mGlobalNames); michael@0: PL_DHashTableFinish(&mNavigatorNames); michael@0: } michael@0: MOZ_COUNT_DTOR(nsScriptNameSpaceManager); michael@0: } michael@0: michael@0: nsGlobalNameStruct * michael@0: nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const nsAString *aKey, michael@0: const char16_t **aClassName) michael@0: { michael@0: GlobalNameMapEntry *entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD)); michael@0: michael@0: if (!entry) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aClassName) { michael@0: *aClassName = entry->mKey.get(); michael@0: } michael@0: michael@0: return &entry->mGlobalName; michael@0: } michael@0: michael@0: void michael@0: nsScriptNameSpaceManager::RemoveFromHash(PLDHashTable *aTable, michael@0: const nsAString *aKey) michael@0: { michael@0: PL_DHashTableOperate(aTable, aKey, PL_DHASH_REMOVE); michael@0: } michael@0: michael@0: nsGlobalNameStruct* michael@0: nsScriptNameSpaceManager::GetConstructorProto(const nsGlobalNameStruct* aStruct) michael@0: { michael@0: NS_ASSERTION(aStruct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias, michael@0: "This function only works on constructor aliases!"); michael@0: if (!aStruct->mAlias->mProto) { michael@0: GlobalNameMapEntry *proto = michael@0: static_cast michael@0: (PL_DHashTableOperate(&mGlobalNames, michael@0: &aStruct->mAlias->mProtoName, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_BUSY(proto)) { michael@0: aStruct->mAlias->mProto = &proto->mGlobalName; michael@0: } michael@0: } michael@0: return aStruct->mAlias->mProto; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::FillHash(nsICategoryManager *aCategoryManager, michael@0: const char *aCategory) michael@0: { michael@0: nsCOMPtr e; michael@0: nsresult rv = aCategoryManager->EnumerateCategory(aCategory, michael@0: getter_AddRefs(e)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr entry; michael@0: while (NS_SUCCEEDED(e->GetNext(getter_AddRefs(entry)))) { michael@0: rv = AddCategoryEntryToHash(aCategoryManager, aCategory, entry); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::RegisterExternalInterfaces(bool aAsProto) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr cm = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr michael@0: iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); michael@0: NS_ENSURE_TRUE(iim, NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: nsCOMPtr enumerator; michael@0: rv = cm->EnumerateCategory(JAVASCRIPT_DOM_INTERFACE, michael@0: getter_AddRefs(enumerator)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsXPIDLCString IID_string; michael@0: nsAutoCString category_entry; michael@0: const char* if_name; michael@0: nsCOMPtr entry; michael@0: nsCOMPtr if_info; michael@0: bool found_old, dom_prefix; michael@0: michael@0: while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) { michael@0: nsCOMPtr category(do_QueryInterface(entry)); michael@0: michael@0: if (!category) { michael@0: NS_WARNING("Category entry not an nsISupportsCString!"); michael@0: michael@0: continue; michael@0: } michael@0: michael@0: rv = category->GetData(category_entry); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = cm->GetCategoryEntry(JAVASCRIPT_DOM_INTERFACE, category_entry.get(), michael@0: getter_Copies(IID_string)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsIID primary_IID; michael@0: if (!primary_IID.Parse(IID_string) || michael@0: primary_IID.Equals(NS_GET_IID(nsISupports))) { michael@0: NS_ERROR("Invalid IID registered with the script namespace manager!"); michael@0: continue; michael@0: } michael@0: michael@0: iim->GetInfoForIID(&primary_IID, getter_AddRefs(if_info)); michael@0: michael@0: while (if_info) { michael@0: const nsIID *iid; michael@0: if_info->GetIIDShared(&iid); michael@0: NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED); michael@0: michael@0: if (iid->Equals(NS_GET_IID(nsISupports))) { michael@0: break; michael@0: } michael@0: michael@0: if_info->GetNameShared(&if_name); michael@0: dom_prefix = (strncmp(if_name, NS_DOM_INTERFACE_PREFIX, michael@0: sizeof(NS_DOM_INTERFACE_PREFIX) - 1) == 0); michael@0: michael@0: const char* name; michael@0: if (dom_prefix) { michael@0: name = if_name + sizeof(NS_DOM_INTERFACE_PREFIX) - 1; michael@0: } else { michael@0: name = if_name + sizeof(NS_INTERFACE_PREFIX) - 1; michael@0: } michael@0: michael@0: if (aAsProto) { michael@0: RegisterClassProto(name, iid, &found_old); michael@0: } else { michael@0: RegisterInterface(name, iid, &found_old); michael@0: } michael@0: michael@0: if (found_old) { michael@0: break; michael@0: } michael@0: michael@0: nsCOMPtr tmp(if_info); michael@0: tmp->GetParent(getter_AddRefs(if_info)); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::RegisterInterface(const char* aIfName, michael@0: const nsIID *aIfIID, michael@0: bool* aFoundOld) michael@0: { michael@0: *aFoundOld = false; michael@0: michael@0: nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aIfName); michael@0: NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: if (s->mType != nsGlobalNameStruct::eTypeNotInitialized && michael@0: s->mType != nsGlobalNameStruct::eTypeNewDOMBinding) { michael@0: *aFoundOld = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: s->mType = nsGlobalNameStruct::eTypeInterface; michael@0: s->mIID = *aIfIID; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #define GLOBALNAME_HASHTABLE_INITIAL_SIZE 1024 michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::Init() michael@0: { michael@0: static const PLDHashTableOps hash_table_ops = michael@0: { michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: GlobalNameHashHashKey, michael@0: GlobalNameHashMatchEntry, michael@0: PL_DHashMoveEntryStub, michael@0: GlobalNameHashClearEntry, michael@0: PL_DHashFinalizeStub, michael@0: GlobalNameHashInitEntry michael@0: }; michael@0: michael@0: mIsInitialized = PL_DHashTableInit(&mGlobalNames, &hash_table_ops, michael@0: nullptr, sizeof(GlobalNameMapEntry), michael@0: GLOBALNAME_HASHTABLE_INITIAL_SIZE, michael@0: fallible_t()); michael@0: NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops, michael@0: nullptr, sizeof(GlobalNameMapEntry), michael@0: GLOBALNAME_HASHTABLE_INITIAL_SIZE, michael@0: fallible_t()); michael@0: if (!mIsInitialized) { michael@0: PL_DHashTableFinish(&mGlobalNames); michael@0: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: RegisterWeakMemoryReporter(this); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: rv = RegisterExternalInterfaces(false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr cm = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = FillHash(cm, JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = FillHash(cm, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = FillHash(cm, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = FillHash(cm, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = FillHash(cm, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Initial filling of the has table has been done. michael@0: // Now, listen for changes. michael@0: nsCOMPtr serv = michael@0: mozilla::services::GetObserverService(); michael@0: michael@0: if (serv) { michael@0: serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, true); michael@0: serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: struct NameSetClosure { michael@0: nsIScriptContext* ctx; michael@0: nsresult rv; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: NameSetInitCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: GlobalNameMapEntry *entry = static_cast(hdr); michael@0: michael@0: if (entry->mGlobalName.mType == nsGlobalNameStruct::eTypeStaticNameSet) { michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr ns = michael@0: do_CreateInstance(entry->mGlobalName.mCID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT); michael@0: michael@0: NameSetClosure *closure = static_cast(arg); michael@0: closure->rv = ns->InitializeNameSet(closure->ctx); michael@0: if (NS_FAILED(closure->rv)) { michael@0: NS_ERROR("Initing external script classes failed!"); michael@0: return PL_DHASH_STOP; michael@0: } michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::InitForContext(nsIScriptContext *aContext) michael@0: { michael@0: NameSetClosure closure; michael@0: closure.ctx = aContext; michael@0: closure.rv = NS_OK; michael@0: PL_DHashTableEnumerate(&mGlobalNames, NameSetInitCallback, &closure); michael@0: michael@0: return closure.rv; michael@0: } michael@0: michael@0: nsGlobalNameStruct* michael@0: nsScriptNameSpaceManager::LookupNameInternal(const nsAString& aName, michael@0: const char16_t **aClassName) michael@0: { michael@0: GlobalNameMapEntry *entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(&mGlobalNames, &aName, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_BUSY(entry)) { michael@0: if (aClassName) { michael@0: *aClassName = entry->mKey.get(); michael@0: } michael@0: return &entry->mGlobalName; michael@0: } michael@0: michael@0: if (aClassName) { michael@0: *aClassName = nullptr; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: const nsGlobalNameStruct* michael@0: nsScriptNameSpaceManager::LookupNavigatorName(const nsAString& aName) michael@0: { michael@0: GlobalNameMapEntry *entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(&mNavigatorNames, &aName, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (!PL_DHASH_ENTRY_IS_BUSY(entry)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return &entry->mGlobalName; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::RegisterClassName(const char *aClassName, michael@0: int32_t aDOMClassInfoID, michael@0: bool aPrivileged, michael@0: bool aXBLAllowed, michael@0: const char16_t **aResult) michael@0: { michael@0: if (!nsCRT::IsAscii(aClassName)) { michael@0: NS_ERROR("Trying to register a non-ASCII class name"); michael@0: return NS_OK; michael@0: } michael@0: nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName, aResult); michael@0: NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: if (s->mType == nsGlobalNameStruct::eTypeClassConstructor) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If a external constructor is already defined with aClassName we michael@0: // won't overwrite it. michael@0: michael@0: if (s->mType == nsGlobalNameStruct::eTypeExternalConstructor) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized || michael@0: s->mType == nsGlobalNameStruct::eTypeNewDOMBinding || michael@0: s->mType == nsGlobalNameStruct::eTypeInterface, michael@0: "Whaaa, JS environment name clash!"); michael@0: michael@0: s->mType = nsGlobalNameStruct::eTypeClassConstructor; michael@0: s->mDOMClassInfoID = aDOMClassInfoID; michael@0: s->mChromeOnly = aPrivileged; michael@0: s->mAllowXBL = aXBLAllowed; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::RegisterClassProto(const char *aClassName, michael@0: const nsIID *aConstructorProtoIID, michael@0: bool *aFoundOld) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aConstructorProtoIID); michael@0: michael@0: *aFoundOld = false; michael@0: michael@0: nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName); michael@0: NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: if (s->mType != nsGlobalNameStruct::eTypeNotInitialized && michael@0: s->mType != nsGlobalNameStruct::eTypeNewDOMBinding && michael@0: s->mType != nsGlobalNameStruct::eTypeInterface) { michael@0: *aFoundOld = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: s->mType = nsGlobalNameStruct::eTypeClassProto; michael@0: s->mIID = *aConstructorProtoIID; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::RegisterExternalClassName(const char *aClassName, michael@0: nsCID& aCID) michael@0: { michael@0: nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName); michael@0: NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: // If an external constructor is already defined with aClassName we michael@0: // won't overwrite it. michael@0: michael@0: if (s->mType == nsGlobalNameStruct::eTypeExternalConstructor) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized || michael@0: s->mType == nsGlobalNameStruct::eTypeNewDOMBinding || michael@0: s->mType == nsGlobalNameStruct::eTypeInterface, michael@0: "Whaaa, JS environment name clash!"); michael@0: michael@0: s->mType = nsGlobalNameStruct::eTypeExternalClassInfoCreator; michael@0: s->mCID = aCID; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::RegisterDOMCIData(const char *aName, michael@0: nsDOMClassInfoExternalConstructorFnc aConstructorFptr, michael@0: const nsIID *aProtoChainInterface, michael@0: const nsIID **aInterfaces, michael@0: uint32_t aScriptableFlags, michael@0: bool aHasClassInterface, michael@0: const nsCID *aConstructorCID) michael@0: { michael@0: const char16_t* className; michael@0: nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aName, &className); michael@0: NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: // If an external constructor is already defined with aClassName we michael@0: // won't overwrite it. michael@0: michael@0: if (s->mType == nsGlobalNameStruct::eTypeClassConstructor || michael@0: s->mType == nsGlobalNameStruct::eTypeExternalClassInfo) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // XXX Should we bail out here? michael@0: NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized || michael@0: s->mType == nsGlobalNameStruct::eTypeNewDOMBinding || michael@0: s->mType == nsGlobalNameStruct::eTypeExternalClassInfoCreator, michael@0: "Someone tries to register classinfo data for a class that isn't new or external!"); michael@0: michael@0: s->mData = new nsExternalDOMClassInfoData; michael@0: NS_ENSURE_TRUE(s->mData, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: s->mType = nsGlobalNameStruct::eTypeExternalClassInfo; michael@0: s->mData->mName = aName; michael@0: s->mData->mNameUTF16 = className; michael@0: if (aConstructorFptr) michael@0: s->mData->u.mExternalConstructorFptr = aConstructorFptr; michael@0: else michael@0: // null constructor will cause us to use nsDOMGenericSH::doCreate michael@0: s->mData->u.mExternalConstructorFptr = nullptr; michael@0: s->mData->mCachedClassInfo = nullptr; michael@0: s->mData->mProtoChainInterface = aProtoChainInterface; michael@0: s->mData->mInterfaces = aInterfaces; michael@0: s->mData->mScriptableFlags = aScriptableFlags; michael@0: s->mData->mHasClassInterface = aHasClassInterface; michael@0: s->mData->mConstructorCID = aConstructorCID; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::OperateCategoryEntryHash(nsICategoryManager* aCategoryManager, michael@0: const char* aCategory, michael@0: nsISupports* aEntry, michael@0: bool aRemove) michael@0: { michael@0: MOZ_ASSERT(aCategoryManager); michael@0: // Get the type from the category name. michael@0: // NOTE: we could have passed the type in FillHash() and guessed it in michael@0: // Observe() but this way, we have only one place to update and this is michael@0: // not performance sensitive. michael@0: nsGlobalNameStruct::nametype type; michael@0: if (strcmp(aCategory, JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY) == 0) { michael@0: type = nsGlobalNameStruct::eTypeExternalConstructor; michael@0: } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY) == 0 || michael@0: strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0) { michael@0: type = nsGlobalNameStruct::eTypeProperty; michael@0: } else if (strcmp(aCategory, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY) == 0) { michael@0: type = nsGlobalNameStruct::eTypeNavigatorProperty; michael@0: } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY) == 0) { michael@0: type = nsGlobalNameStruct::eTypeStaticNameSet; michael@0: } else { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr strWrapper = do_QueryInterface(aEntry); michael@0: michael@0: if (!strWrapper) { michael@0: NS_WARNING("Category entry not an nsISupportsCString!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoCString categoryEntry; michael@0: nsresult rv = strWrapper->GetData(categoryEntry); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: PLDHashTable *table; michael@0: if (type == nsGlobalNameStruct::eTypeNavigatorProperty) { michael@0: table = &mNavigatorNames; michael@0: } else { michael@0: table = &mGlobalNames; michael@0: } michael@0: michael@0: // We need to handle removal before calling GetCategoryEntry michael@0: // because the category entry is already removed before we are michael@0: // notified. michael@0: if (aRemove) { michael@0: NS_ConvertASCIItoUTF16 entry(categoryEntry); michael@0: const nsGlobalNameStruct *s = michael@0: type == nsGlobalNameStruct::eTypeNavigatorProperty ? michael@0: LookupNavigatorName(entry) : LookupNameInternal(entry); michael@0: // Verify mType so that this API doesn't remove names michael@0: // registered by others. michael@0: if (!s || s->mType != type) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: RemoveFromHash(table, &entry); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsXPIDLCString contractId; michael@0: rv = aCategoryManager->GetCategoryEntry(aCategory, categoryEntry.get(), michael@0: getter_Copies(contractId)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (type == nsGlobalNameStruct::eTypeNavigatorProperty || michael@0: type == nsGlobalNameStruct::eTypeExternalConstructor) { michael@0: bool isNavProperty = type == nsGlobalNameStruct::eTypeNavigatorProperty; michael@0: nsPrintfCString prefName("dom.%s.disable.%s", michael@0: isNavProperty ? "navigator-property" : "global-constructor", michael@0: categoryEntry.get()); michael@0: if (Preferences::GetType(prefName.get()) == nsIPrefBranch::PREF_BOOL && michael@0: Preferences::GetBool(prefName.get(), false)) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr registrar; michael@0: rv = NS_GetComponentRegistrar(getter_AddRefs(registrar)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCID *cidPtr; michael@0: rv = registrar->ContractIDToCID(contractId, &cidPtr); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Bad contract id registed with the script namespace manager"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Copy CID onto the stack, so we can free it right away and avoid having michael@0: // to add cleanup code at every exit point from this function. michael@0: nsCID cid = *cidPtr; michael@0: nsMemory::Free(cidPtr); michael@0: michael@0: if (type == nsGlobalNameStruct::eTypeExternalConstructor) { michael@0: nsXPIDLCString constructorProto; michael@0: rv = aCategoryManager->GetCategoryEntry(JAVASCRIPT_GLOBAL_CONSTRUCTOR_PROTO_ALIAS_CATEGORY, michael@0: categoryEntry.get(), michael@0: getter_Copies(constructorProto)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsGlobalNameStruct *s = AddToHash(&mGlobalNames, categoryEntry.get()); michael@0: NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: if (s->mType == nsGlobalNameStruct::eTypeNotInitialized || michael@0: s->mType == nsGlobalNameStruct::eTypeNewDOMBinding) { michael@0: s->mAlias = new nsGlobalNameStruct::ConstructorAlias; michael@0: s->mType = nsGlobalNameStruct::eTypeExternalConstructorAlias; michael@0: s->mChromeOnly = false; michael@0: s->mAlias->mCID = cid; michael@0: AppendASCIItoUTF16(constructorProto, s->mAlias->mProtoName); michael@0: s->mAlias->mProto = nullptr; michael@0: } else { michael@0: NS_WARNING("Global script name not overwritten!"); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: nsGlobalNameStruct *s = AddToHash(table, categoryEntry.get()); michael@0: NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: if (s->mType == nsGlobalNameStruct::eTypeNotInitialized || michael@0: s->mType == nsGlobalNameStruct::eTypeNewDOMBinding) { michael@0: s->mType = type; michael@0: s->mCID = cid; michael@0: s->mChromeOnly = michael@0: strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0; michael@0: } else { michael@0: NS_WARNING("Global script name not overwritten!"); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryManager, michael@0: const char* aCategory, michael@0: nsISupports* aEntry) michael@0: { michael@0: return OperateCategoryEntryHash(aCategoryManager, aCategory, aEntry, michael@0: /* aRemove = */ false); michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptNameSpaceManager::RemoveCategoryEntryFromHash(nsICategoryManager* aCategoryManager, michael@0: const char* aCategory, michael@0: nsISupports* aEntry) michael@0: { michael@0: return OperateCategoryEntryHash(aCategoryManager, aCategory, aEntry, michael@0: /* aRemove = */ true); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptNameSpaceManager::Observe(nsISupports* aSubject, const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (!aData) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID)) { michael@0: nsCOMPtr cm = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID); michael@0: if (!cm) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return AddCategoryEntryToHash(cm, NS_ConvertUTF16toUTF8(aData).get(), michael@0: aSubject); michael@0: } else if (!strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID)) { michael@0: nsCOMPtr cm = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID); michael@0: if (!cm) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: return RemoveCategoryEntryFromHash(cm, NS_ConvertUTF16toUTF8(aData).get(), michael@0: aSubject); michael@0: } michael@0: michael@0: // TODO: we could observe NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID michael@0: // but we are safe without it. See bug 600460. michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsScriptNameSpaceManager::RegisterDefineDOMInterface(const nsAFlatString& aName, michael@0: mozilla::dom::DefineInterface aDefineDOMInterface, michael@0: mozilla::dom::ConstructorEnabled* aConstructorEnabled) michael@0: { michael@0: nsGlobalNameStruct *s = AddToHash(&mGlobalNames, &aName); michael@0: if (s) { michael@0: if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) { michael@0: s->mType = nsGlobalNameStruct::eTypeNewDOMBinding; michael@0: } michael@0: s->mDefineDOMInterface = aDefineDOMInterface; michael@0: s->mConstructorEnabled = aConstructorEnabled; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsScriptNameSpaceManager::RegisterNavigatorDOMConstructor( michael@0: const nsAFlatString& aName, michael@0: mozilla::dom::ConstructNavigatorProperty aNavConstructor, michael@0: mozilla::dom::ConstructorEnabled* aConstructorEnabled) michael@0: { michael@0: nsGlobalNameStruct *s = AddToHash(&mNavigatorNames, &aName); michael@0: if (s) { michael@0: if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) { michael@0: s->mType = nsGlobalNameStruct::eTypeNewDOMBinding; michael@0: } michael@0: s->mConstructNavigatorProperty = aNavConstructor; michael@0: s->mConstructorEnabled = aConstructorEnabled; michael@0: } michael@0: } michael@0: michael@0: struct NameClosure michael@0: { michael@0: nsScriptNameSpaceManager::NameEnumerator enumerator; michael@0: void* closure; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: EnumerateName(PLDHashTable*, PLDHashEntryHdr *hdr, uint32_t, void* aClosure) michael@0: { michael@0: GlobalNameMapEntry *entry = static_cast(hdr); michael@0: NameClosure* closure = static_cast(aClosure); michael@0: return closure->enumerator(entry->mKey, entry->mGlobalName, closure->closure); michael@0: } michael@0: michael@0: void michael@0: nsScriptNameSpaceManager::EnumerateGlobalNames(NameEnumerator aEnumerator, michael@0: void* aClosure) michael@0: { michael@0: NameClosure closure = { aEnumerator, aClosure }; michael@0: PL_DHashTableEnumerate(&mGlobalNames, EnumerateName, &closure); michael@0: } michael@0: michael@0: void michael@0: nsScriptNameSpaceManager::EnumerateNavigatorNames(NameEnumerator aEnumerator, michael@0: void* aClosure) michael@0: { michael@0: NameClosure closure = { aEnumerator, aClosure }; michael@0: PL_DHashTableEnumerate(&mNavigatorNames, EnumerateName, &closure); michael@0: } michael@0: michael@0: static size_t michael@0: SizeOfEntryExcludingThis(PLDHashEntryHdr *aHdr, MallocSizeOf aMallocSizeOf, michael@0: void *aArg) michael@0: { michael@0: GlobalNameMapEntry* entry = static_cast(aHdr); michael@0: return entry->SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: MOZ_DEFINE_MALLOC_SIZE_OF(ScriptNameSpaceManagerMallocSizeOf) michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptNameSpaceManager::CollectReports( michael@0: nsIHandleReportCallback* aHandleReport, nsISupports* aData) michael@0: { michael@0: return MOZ_COLLECT_REPORT( michael@0: "explicit/script-namespace-manager", KIND_HEAP, UNITS_BYTES, michael@0: SizeOfIncludingThis(ScriptNameSpaceManagerMallocSizeOf), michael@0: "Memory used for the script namespace manager."); michael@0: } michael@0: michael@0: size_t michael@0: nsScriptNameSpaceManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) michael@0: { michael@0: size_t n = 0; michael@0: n += PL_DHashTableSizeOfExcludingThis(&mGlobalNames, michael@0: SizeOfEntryExcludingThis, aMallocSizeOf); michael@0: n += PL_DHashTableSizeOfExcludingThis(&mNavigatorNames, michael@0: SizeOfEntryExcludingThis, aMallocSizeOf); michael@0: return n; michael@0: }