Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsScriptNameSpaceManager.h"
8 #include "nsCOMPtr.h"
9 #include "nsIComponentManager.h"
10 #include "nsIComponentRegistrar.h"
11 #include "nsICategoryManager.h"
12 #include "nsIServiceManager.h"
13 #include "nsXPCOM.h"
14 #include "nsISupportsPrimitives.h"
15 #include "nsIScriptExternalNameSet.h"
16 #include "nsIScriptNameSpaceManager.h"
17 #include "nsIScriptContext.h"
18 #include "nsIInterfaceInfoManager.h"
19 #include "nsIInterfaceInfo.h"
20 #include "xptinfo.h"
21 #include "nsXPIDLString.h"
22 #include "nsPrintfCString.h"
23 #include "nsReadableUtils.h"
24 #include "nsHashKeys.h"
25 #include "nsDOMClassInfo.h"
26 #include "nsCRT.h"
27 #include "nsIObserverService.h"
28 #include "nsISimpleEnumerator.h"
30 #include "mozilla/MemoryReporting.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/Services.h"
34 #define NS_INTERFACE_PREFIX "nsI"
35 #define NS_DOM_INTERFACE_PREFIX "nsIDOM"
37 using namespace mozilla;
39 // Our extended PLDHashEntryHdr
40 class GlobalNameMapEntry : public PLDHashEntryHdr
41 {
42 public:
43 // Our hash table ops don't care about the order of these members
44 nsString mKey;
45 nsGlobalNameStruct mGlobalName;
47 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
48 // Measurement of the following members may be added later if DMD finds it
49 // is worthwhile:
50 // - mGlobalName
51 return mKey.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf);
52 }
53 };
56 static PLDHashNumber
57 GlobalNameHashHashKey(PLDHashTable *table, const void *key)
58 {
59 const nsAString *str = static_cast<const nsAString *>(key);
60 return HashString(*str);
61 }
63 static bool
64 GlobalNameHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
65 const void *key)
66 {
67 const GlobalNameMapEntry *e =
68 static_cast<const GlobalNameMapEntry *>(entry);
69 const nsAString *str = static_cast<const nsAString *>(key);
71 return str->Equals(e->mKey);
72 }
74 static void
75 GlobalNameHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
76 {
77 GlobalNameMapEntry *e = static_cast<GlobalNameMapEntry *>(entry);
79 // An entry is being cleared, let the key (nsString) do its own
80 // cleanup.
81 e->mKey.~nsString();
82 if (e->mGlobalName.mType == nsGlobalNameStruct::eTypeExternalClassInfo) {
83 nsIClassInfo* ci = GET_CLEAN_CI_PTR(e->mGlobalName.mData->mCachedClassInfo);
85 // If we constructed an internal helper, we'll let the helper delete
86 // the nsDOMClassInfoData structure, if not we do it here.
87 if (!ci || e->mGlobalName.mData->u.mExternalConstructorFptr) {
88 delete e->mGlobalName.mData;
89 }
91 // Release our pointer to the helper.
92 NS_IF_RELEASE(ci);
93 }
94 else if (e->mGlobalName.mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
95 delete e->mGlobalName.mAlias;
96 }
98 // This will set e->mGlobalName.mType to
99 // nsGlobalNameStruct::eTypeNotInitialized
100 memset(&e->mGlobalName, 0, sizeof(nsGlobalNameStruct));
101 }
103 static bool
104 GlobalNameHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
105 const void *key)
106 {
107 GlobalNameMapEntry *e = static_cast<GlobalNameMapEntry *>(entry);
108 const nsAString *keyStr = static_cast<const nsAString *>(key);
110 // Initialize the key in the entry with placement new
111 new (&e->mKey) nsString(*keyStr);
113 // This will set e->mGlobalName.mType to
114 // nsGlobalNameStruct::eTypeNotInitialized
115 memset(&e->mGlobalName, 0, sizeof(nsGlobalNameStruct));
116 return true;
117 }
119 NS_IMPL_ISUPPORTS(
120 nsScriptNameSpaceManager,
121 nsIObserver,
122 nsISupportsWeakReference,
123 nsIMemoryReporter)
125 nsScriptNameSpaceManager::nsScriptNameSpaceManager()
126 : mIsInitialized(false)
127 {
128 MOZ_COUNT_CTOR(nsScriptNameSpaceManager);
129 }
131 nsScriptNameSpaceManager::~nsScriptNameSpaceManager()
132 {
133 if (mIsInitialized) {
134 UnregisterWeakMemoryReporter(this);
135 // Destroy the hash
136 PL_DHashTableFinish(&mGlobalNames);
137 PL_DHashTableFinish(&mNavigatorNames);
138 }
139 MOZ_COUNT_DTOR(nsScriptNameSpaceManager);
140 }
142 nsGlobalNameStruct *
143 nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const nsAString *aKey,
144 const char16_t **aClassName)
145 {
146 GlobalNameMapEntry *entry =
147 static_cast<GlobalNameMapEntry *>
148 (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
150 if (!entry) {
151 return nullptr;
152 }
154 if (aClassName) {
155 *aClassName = entry->mKey.get();
156 }
158 return &entry->mGlobalName;
159 }
161 void
162 nsScriptNameSpaceManager::RemoveFromHash(PLDHashTable *aTable,
163 const nsAString *aKey)
164 {
165 PL_DHashTableOperate(aTable, aKey, PL_DHASH_REMOVE);
166 }
168 nsGlobalNameStruct*
169 nsScriptNameSpaceManager::GetConstructorProto(const nsGlobalNameStruct* aStruct)
170 {
171 NS_ASSERTION(aStruct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias,
172 "This function only works on constructor aliases!");
173 if (!aStruct->mAlias->mProto) {
174 GlobalNameMapEntry *proto =
175 static_cast<GlobalNameMapEntry *>
176 (PL_DHashTableOperate(&mGlobalNames,
177 &aStruct->mAlias->mProtoName,
178 PL_DHASH_LOOKUP));
180 if (PL_DHASH_ENTRY_IS_BUSY(proto)) {
181 aStruct->mAlias->mProto = &proto->mGlobalName;
182 }
183 }
184 return aStruct->mAlias->mProto;
185 }
187 nsresult
188 nsScriptNameSpaceManager::FillHash(nsICategoryManager *aCategoryManager,
189 const char *aCategory)
190 {
191 nsCOMPtr<nsISimpleEnumerator> e;
192 nsresult rv = aCategoryManager->EnumerateCategory(aCategory,
193 getter_AddRefs(e));
194 NS_ENSURE_SUCCESS(rv, rv);
196 nsCOMPtr<nsISupports> entry;
197 while (NS_SUCCEEDED(e->GetNext(getter_AddRefs(entry)))) {
198 rv = AddCategoryEntryToHash(aCategoryManager, aCategory, entry);
199 if (NS_FAILED(rv)) {
200 return rv;
201 }
202 }
204 return NS_OK;
205 }
208 nsresult
209 nsScriptNameSpaceManager::RegisterExternalInterfaces(bool aAsProto)
210 {
211 nsresult rv;
212 nsCOMPtr<nsICategoryManager> cm =
213 do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
214 NS_ENSURE_SUCCESS(rv, rv);
216 nsCOMPtr<nsIInterfaceInfoManager>
217 iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
218 NS_ENSURE_TRUE(iim, NS_ERROR_NOT_AVAILABLE);
220 nsCOMPtr<nsISimpleEnumerator> enumerator;
221 rv = cm->EnumerateCategory(JAVASCRIPT_DOM_INTERFACE,
222 getter_AddRefs(enumerator));
223 NS_ENSURE_SUCCESS(rv, rv);
225 nsXPIDLCString IID_string;
226 nsAutoCString category_entry;
227 const char* if_name;
228 nsCOMPtr<nsISupports> entry;
229 nsCOMPtr<nsIInterfaceInfo> if_info;
230 bool found_old, dom_prefix;
232 while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) {
233 nsCOMPtr<nsISupportsCString> category(do_QueryInterface(entry));
235 if (!category) {
236 NS_WARNING("Category entry not an nsISupportsCString!");
238 continue;
239 }
241 rv = category->GetData(category_entry);
242 NS_ENSURE_SUCCESS(rv, rv);
244 rv = cm->GetCategoryEntry(JAVASCRIPT_DOM_INTERFACE, category_entry.get(),
245 getter_Copies(IID_string));
246 NS_ENSURE_SUCCESS(rv, rv);
248 nsIID primary_IID;
249 if (!primary_IID.Parse(IID_string) ||
250 primary_IID.Equals(NS_GET_IID(nsISupports))) {
251 NS_ERROR("Invalid IID registered with the script namespace manager!");
252 continue;
253 }
255 iim->GetInfoForIID(&primary_IID, getter_AddRefs(if_info));
257 while (if_info) {
258 const nsIID *iid;
259 if_info->GetIIDShared(&iid);
260 NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
262 if (iid->Equals(NS_GET_IID(nsISupports))) {
263 break;
264 }
266 if_info->GetNameShared(&if_name);
267 dom_prefix = (strncmp(if_name, NS_DOM_INTERFACE_PREFIX,
268 sizeof(NS_DOM_INTERFACE_PREFIX) - 1) == 0);
270 const char* name;
271 if (dom_prefix) {
272 name = if_name + sizeof(NS_DOM_INTERFACE_PREFIX) - 1;
273 } else {
274 name = if_name + sizeof(NS_INTERFACE_PREFIX) - 1;
275 }
277 if (aAsProto) {
278 RegisterClassProto(name, iid, &found_old);
279 } else {
280 RegisterInterface(name, iid, &found_old);
281 }
283 if (found_old) {
284 break;
285 }
287 nsCOMPtr<nsIInterfaceInfo> tmp(if_info);
288 tmp->GetParent(getter_AddRefs(if_info));
289 }
290 }
292 return NS_OK;
293 }
295 nsresult
296 nsScriptNameSpaceManager::RegisterInterface(const char* aIfName,
297 const nsIID *aIfIID,
298 bool* aFoundOld)
299 {
300 *aFoundOld = false;
302 nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aIfName);
303 NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
305 if (s->mType != nsGlobalNameStruct::eTypeNotInitialized &&
306 s->mType != nsGlobalNameStruct::eTypeNewDOMBinding) {
307 *aFoundOld = true;
309 return NS_OK;
310 }
312 s->mType = nsGlobalNameStruct::eTypeInterface;
313 s->mIID = *aIfIID;
315 return NS_OK;
316 }
318 #define GLOBALNAME_HASHTABLE_INITIAL_SIZE 1024
320 nsresult
321 nsScriptNameSpaceManager::Init()
322 {
323 static const PLDHashTableOps hash_table_ops =
324 {
325 PL_DHashAllocTable,
326 PL_DHashFreeTable,
327 GlobalNameHashHashKey,
328 GlobalNameHashMatchEntry,
329 PL_DHashMoveEntryStub,
330 GlobalNameHashClearEntry,
331 PL_DHashFinalizeStub,
332 GlobalNameHashInitEntry
333 };
335 mIsInitialized = PL_DHashTableInit(&mGlobalNames, &hash_table_ops,
336 nullptr, sizeof(GlobalNameMapEntry),
337 GLOBALNAME_HASHTABLE_INITIAL_SIZE,
338 fallible_t());
339 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY);
341 mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops,
342 nullptr, sizeof(GlobalNameMapEntry),
343 GLOBALNAME_HASHTABLE_INITIAL_SIZE,
344 fallible_t());
345 if (!mIsInitialized) {
346 PL_DHashTableFinish(&mGlobalNames);
348 return NS_ERROR_OUT_OF_MEMORY;
349 }
351 RegisterWeakMemoryReporter(this);
353 nsresult rv = NS_OK;
355 rv = RegisterExternalInterfaces(false);
356 NS_ENSURE_SUCCESS(rv, rv);
358 nsCOMPtr<nsICategoryManager> cm =
359 do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
360 NS_ENSURE_SUCCESS(rv, rv);
362 rv = FillHash(cm, JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY);
363 NS_ENSURE_SUCCESS(rv, rv);
365 rv = FillHash(cm, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY);
366 NS_ENSURE_SUCCESS(rv, rv);
368 rv = FillHash(cm, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY);
369 NS_ENSURE_SUCCESS(rv, rv);
371 rv = FillHash(cm, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY);
372 NS_ENSURE_SUCCESS(rv, rv);
374 rv = FillHash(cm, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY);
375 NS_ENSURE_SUCCESS(rv, rv);
377 // Initial filling of the has table has been done.
378 // Now, listen for changes.
379 nsCOMPtr<nsIObserverService> serv =
380 mozilla::services::GetObserverService();
382 if (serv) {
383 serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, true);
384 serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, true);
385 }
387 return NS_OK;
388 }
390 struct NameSetClosure {
391 nsIScriptContext* ctx;
392 nsresult rv;
393 };
395 static PLDHashOperator
396 NameSetInitCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
397 uint32_t number, void *arg)
398 {
399 GlobalNameMapEntry *entry = static_cast<GlobalNameMapEntry *>(hdr);
401 if (entry->mGlobalName.mType == nsGlobalNameStruct::eTypeStaticNameSet) {
402 nsresult rv = NS_OK;
403 nsCOMPtr<nsIScriptExternalNameSet> ns =
404 do_CreateInstance(entry->mGlobalName.mCID, &rv);
405 NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
407 NameSetClosure *closure = static_cast<NameSetClosure *>(arg);
408 closure->rv = ns->InitializeNameSet(closure->ctx);
409 if (NS_FAILED(closure->rv)) {
410 NS_ERROR("Initing external script classes failed!");
411 return PL_DHASH_STOP;
412 }
413 }
415 return PL_DHASH_NEXT;
416 }
418 nsresult
419 nsScriptNameSpaceManager::InitForContext(nsIScriptContext *aContext)
420 {
421 NameSetClosure closure;
422 closure.ctx = aContext;
423 closure.rv = NS_OK;
424 PL_DHashTableEnumerate(&mGlobalNames, NameSetInitCallback, &closure);
426 return closure.rv;
427 }
429 nsGlobalNameStruct*
430 nsScriptNameSpaceManager::LookupNameInternal(const nsAString& aName,
431 const char16_t **aClassName)
432 {
433 GlobalNameMapEntry *entry =
434 static_cast<GlobalNameMapEntry *>
435 (PL_DHashTableOperate(&mGlobalNames, &aName,
436 PL_DHASH_LOOKUP));
438 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
439 if (aClassName) {
440 *aClassName = entry->mKey.get();
441 }
442 return &entry->mGlobalName;
443 }
445 if (aClassName) {
446 *aClassName = nullptr;
447 }
448 return nullptr;
449 }
451 const nsGlobalNameStruct*
452 nsScriptNameSpaceManager::LookupNavigatorName(const nsAString& aName)
453 {
454 GlobalNameMapEntry *entry =
455 static_cast<GlobalNameMapEntry *>
456 (PL_DHashTableOperate(&mNavigatorNames, &aName,
457 PL_DHASH_LOOKUP));
459 if (!PL_DHASH_ENTRY_IS_BUSY(entry)) {
460 return nullptr;
461 }
463 return &entry->mGlobalName;
464 }
466 nsresult
467 nsScriptNameSpaceManager::RegisterClassName(const char *aClassName,
468 int32_t aDOMClassInfoID,
469 bool aPrivileged,
470 bool aXBLAllowed,
471 const char16_t **aResult)
472 {
473 if (!nsCRT::IsAscii(aClassName)) {
474 NS_ERROR("Trying to register a non-ASCII class name");
475 return NS_OK;
476 }
477 nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName, aResult);
478 NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
480 if (s->mType == nsGlobalNameStruct::eTypeClassConstructor) {
481 return NS_OK;
482 }
484 // If a external constructor is already defined with aClassName we
485 // won't overwrite it.
487 if (s->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
488 return NS_OK;
489 }
491 NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized ||
492 s->mType == nsGlobalNameStruct::eTypeNewDOMBinding ||
493 s->mType == nsGlobalNameStruct::eTypeInterface,
494 "Whaaa, JS environment name clash!");
496 s->mType = nsGlobalNameStruct::eTypeClassConstructor;
497 s->mDOMClassInfoID = aDOMClassInfoID;
498 s->mChromeOnly = aPrivileged;
499 s->mAllowXBL = aXBLAllowed;
501 return NS_OK;
502 }
504 nsresult
505 nsScriptNameSpaceManager::RegisterClassProto(const char *aClassName,
506 const nsIID *aConstructorProtoIID,
507 bool *aFoundOld)
508 {
509 NS_ENSURE_ARG_POINTER(aConstructorProtoIID);
511 *aFoundOld = false;
513 nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName);
514 NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
516 if (s->mType != nsGlobalNameStruct::eTypeNotInitialized &&
517 s->mType != nsGlobalNameStruct::eTypeNewDOMBinding &&
518 s->mType != nsGlobalNameStruct::eTypeInterface) {
519 *aFoundOld = true;
521 return NS_OK;
522 }
524 s->mType = nsGlobalNameStruct::eTypeClassProto;
525 s->mIID = *aConstructorProtoIID;
527 return NS_OK;
528 }
530 nsresult
531 nsScriptNameSpaceManager::RegisterExternalClassName(const char *aClassName,
532 nsCID& aCID)
533 {
534 nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName);
535 NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
537 // If an external constructor is already defined with aClassName we
538 // won't overwrite it.
540 if (s->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
541 return NS_OK;
542 }
544 NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized ||
545 s->mType == nsGlobalNameStruct::eTypeNewDOMBinding ||
546 s->mType == nsGlobalNameStruct::eTypeInterface,
547 "Whaaa, JS environment name clash!");
549 s->mType = nsGlobalNameStruct::eTypeExternalClassInfoCreator;
550 s->mCID = aCID;
552 return NS_OK;
553 }
555 nsresult
556 nsScriptNameSpaceManager::RegisterDOMCIData(const char *aName,
557 nsDOMClassInfoExternalConstructorFnc aConstructorFptr,
558 const nsIID *aProtoChainInterface,
559 const nsIID **aInterfaces,
560 uint32_t aScriptableFlags,
561 bool aHasClassInterface,
562 const nsCID *aConstructorCID)
563 {
564 const char16_t* className;
565 nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aName, &className);
566 NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
568 // If an external constructor is already defined with aClassName we
569 // won't overwrite it.
571 if (s->mType == nsGlobalNameStruct::eTypeClassConstructor ||
572 s->mType == nsGlobalNameStruct::eTypeExternalClassInfo) {
573 return NS_OK;
574 }
576 // XXX Should we bail out here?
577 NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized ||
578 s->mType == nsGlobalNameStruct::eTypeNewDOMBinding ||
579 s->mType == nsGlobalNameStruct::eTypeExternalClassInfoCreator,
580 "Someone tries to register classinfo data for a class that isn't new or external!");
582 s->mData = new nsExternalDOMClassInfoData;
583 NS_ENSURE_TRUE(s->mData, NS_ERROR_OUT_OF_MEMORY);
585 s->mType = nsGlobalNameStruct::eTypeExternalClassInfo;
586 s->mData->mName = aName;
587 s->mData->mNameUTF16 = className;
588 if (aConstructorFptr)
589 s->mData->u.mExternalConstructorFptr = aConstructorFptr;
590 else
591 // null constructor will cause us to use nsDOMGenericSH::doCreate
592 s->mData->u.mExternalConstructorFptr = nullptr;
593 s->mData->mCachedClassInfo = nullptr;
594 s->mData->mProtoChainInterface = aProtoChainInterface;
595 s->mData->mInterfaces = aInterfaces;
596 s->mData->mScriptableFlags = aScriptableFlags;
597 s->mData->mHasClassInterface = aHasClassInterface;
598 s->mData->mConstructorCID = aConstructorCID;
600 return NS_OK;
601 }
603 nsresult
604 nsScriptNameSpaceManager::OperateCategoryEntryHash(nsICategoryManager* aCategoryManager,
605 const char* aCategory,
606 nsISupports* aEntry,
607 bool aRemove)
608 {
609 MOZ_ASSERT(aCategoryManager);
610 // Get the type from the category name.
611 // NOTE: we could have passed the type in FillHash() and guessed it in
612 // Observe() but this way, we have only one place to update and this is
613 // not performance sensitive.
614 nsGlobalNameStruct::nametype type;
615 if (strcmp(aCategory, JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY) == 0) {
616 type = nsGlobalNameStruct::eTypeExternalConstructor;
617 } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY) == 0 ||
618 strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0) {
619 type = nsGlobalNameStruct::eTypeProperty;
620 } else if (strcmp(aCategory, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY) == 0) {
621 type = nsGlobalNameStruct::eTypeNavigatorProperty;
622 } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY) == 0) {
623 type = nsGlobalNameStruct::eTypeStaticNameSet;
624 } else {
625 return NS_OK;
626 }
628 nsCOMPtr<nsISupportsCString> strWrapper = do_QueryInterface(aEntry);
630 if (!strWrapper) {
631 NS_WARNING("Category entry not an nsISupportsCString!");
632 return NS_OK;
633 }
635 nsAutoCString categoryEntry;
636 nsresult rv = strWrapper->GetData(categoryEntry);
637 NS_ENSURE_SUCCESS(rv, rv);
639 PLDHashTable *table;
640 if (type == nsGlobalNameStruct::eTypeNavigatorProperty) {
641 table = &mNavigatorNames;
642 } else {
643 table = &mGlobalNames;
644 }
646 // We need to handle removal before calling GetCategoryEntry
647 // because the category entry is already removed before we are
648 // notified.
649 if (aRemove) {
650 NS_ConvertASCIItoUTF16 entry(categoryEntry);
651 const nsGlobalNameStruct *s =
652 type == nsGlobalNameStruct::eTypeNavigatorProperty ?
653 LookupNavigatorName(entry) : LookupNameInternal(entry);
654 // Verify mType so that this API doesn't remove names
655 // registered by others.
656 if (!s || s->mType != type) {
657 return NS_OK;
658 }
660 RemoveFromHash(table, &entry);
661 return NS_OK;
662 }
664 nsXPIDLCString contractId;
665 rv = aCategoryManager->GetCategoryEntry(aCategory, categoryEntry.get(),
666 getter_Copies(contractId));
667 NS_ENSURE_SUCCESS(rv, rv);
669 if (type == nsGlobalNameStruct::eTypeNavigatorProperty ||
670 type == nsGlobalNameStruct::eTypeExternalConstructor) {
671 bool isNavProperty = type == nsGlobalNameStruct::eTypeNavigatorProperty;
672 nsPrintfCString prefName("dom.%s.disable.%s",
673 isNavProperty ? "navigator-property" : "global-constructor",
674 categoryEntry.get());
675 if (Preferences::GetType(prefName.get()) == nsIPrefBranch::PREF_BOOL &&
676 Preferences::GetBool(prefName.get(), false)) {
677 return NS_OK;
678 }
679 }
681 nsCOMPtr<nsIComponentRegistrar> registrar;
682 rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
683 NS_ENSURE_SUCCESS(rv, rv);
685 nsCID *cidPtr;
686 rv = registrar->ContractIDToCID(contractId, &cidPtr);
688 if (NS_FAILED(rv)) {
689 NS_WARNING("Bad contract id registed with the script namespace manager");
690 return NS_OK;
691 }
693 // Copy CID onto the stack, so we can free it right away and avoid having
694 // to add cleanup code at every exit point from this function.
695 nsCID cid = *cidPtr;
696 nsMemory::Free(cidPtr);
698 if (type == nsGlobalNameStruct::eTypeExternalConstructor) {
699 nsXPIDLCString constructorProto;
700 rv = aCategoryManager->GetCategoryEntry(JAVASCRIPT_GLOBAL_CONSTRUCTOR_PROTO_ALIAS_CATEGORY,
701 categoryEntry.get(),
702 getter_Copies(constructorProto));
703 if (NS_SUCCEEDED(rv)) {
704 nsGlobalNameStruct *s = AddToHash(&mGlobalNames, categoryEntry.get());
705 NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
707 if (s->mType == nsGlobalNameStruct::eTypeNotInitialized ||
708 s->mType == nsGlobalNameStruct::eTypeNewDOMBinding) {
709 s->mAlias = new nsGlobalNameStruct::ConstructorAlias;
710 s->mType = nsGlobalNameStruct::eTypeExternalConstructorAlias;
711 s->mChromeOnly = false;
712 s->mAlias->mCID = cid;
713 AppendASCIItoUTF16(constructorProto, s->mAlias->mProtoName);
714 s->mAlias->mProto = nullptr;
715 } else {
716 NS_WARNING("Global script name not overwritten!");
717 }
719 return NS_OK;
720 }
721 }
723 nsGlobalNameStruct *s = AddToHash(table, categoryEntry.get());
724 NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
726 if (s->mType == nsGlobalNameStruct::eTypeNotInitialized ||
727 s->mType == nsGlobalNameStruct::eTypeNewDOMBinding) {
728 s->mType = type;
729 s->mCID = cid;
730 s->mChromeOnly =
731 strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0;
732 } else {
733 NS_WARNING("Global script name not overwritten!");
734 }
736 return NS_OK;
737 }
739 nsresult
740 nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryManager,
741 const char* aCategory,
742 nsISupports* aEntry)
743 {
744 return OperateCategoryEntryHash(aCategoryManager, aCategory, aEntry,
745 /* aRemove = */ false);
746 }
748 nsresult
749 nsScriptNameSpaceManager::RemoveCategoryEntryFromHash(nsICategoryManager* aCategoryManager,
750 const char* aCategory,
751 nsISupports* aEntry)
752 {
753 return OperateCategoryEntryHash(aCategoryManager, aCategory, aEntry,
754 /* aRemove = */ true);
755 }
757 NS_IMETHODIMP
758 nsScriptNameSpaceManager::Observe(nsISupports* aSubject, const char* aTopic,
759 const char16_t* aData)
760 {
761 if (!aData) {
762 return NS_OK;
763 }
765 if (!strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID)) {
766 nsCOMPtr<nsICategoryManager> cm =
767 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
768 if (!cm) {
769 return NS_OK;
770 }
772 return AddCategoryEntryToHash(cm, NS_ConvertUTF16toUTF8(aData).get(),
773 aSubject);
774 } else if (!strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID)) {
775 nsCOMPtr<nsICategoryManager> cm =
776 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
777 if (!cm) {
778 return NS_OK;
779 }
781 return RemoveCategoryEntryFromHash(cm, NS_ConvertUTF16toUTF8(aData).get(),
782 aSubject);
783 }
785 // TODO: we could observe NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID
786 // but we are safe without it. See bug 600460.
788 return NS_OK;
789 }
791 void
792 nsScriptNameSpaceManager::RegisterDefineDOMInterface(const nsAFlatString& aName,
793 mozilla::dom::DefineInterface aDefineDOMInterface,
794 mozilla::dom::ConstructorEnabled* aConstructorEnabled)
795 {
796 nsGlobalNameStruct *s = AddToHash(&mGlobalNames, &aName);
797 if (s) {
798 if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {
799 s->mType = nsGlobalNameStruct::eTypeNewDOMBinding;
800 }
801 s->mDefineDOMInterface = aDefineDOMInterface;
802 s->mConstructorEnabled = aConstructorEnabled;
803 }
804 }
806 void
807 nsScriptNameSpaceManager::RegisterNavigatorDOMConstructor(
808 const nsAFlatString& aName,
809 mozilla::dom::ConstructNavigatorProperty aNavConstructor,
810 mozilla::dom::ConstructorEnabled* aConstructorEnabled)
811 {
812 nsGlobalNameStruct *s = AddToHash(&mNavigatorNames, &aName);
813 if (s) {
814 if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {
815 s->mType = nsGlobalNameStruct::eTypeNewDOMBinding;
816 }
817 s->mConstructNavigatorProperty = aNavConstructor;
818 s->mConstructorEnabled = aConstructorEnabled;
819 }
820 }
822 struct NameClosure
823 {
824 nsScriptNameSpaceManager::NameEnumerator enumerator;
825 void* closure;
826 };
828 static PLDHashOperator
829 EnumerateName(PLDHashTable*, PLDHashEntryHdr *hdr, uint32_t, void* aClosure)
830 {
831 GlobalNameMapEntry *entry = static_cast<GlobalNameMapEntry *>(hdr);
832 NameClosure* closure = static_cast<NameClosure*>(aClosure);
833 return closure->enumerator(entry->mKey, entry->mGlobalName, closure->closure);
834 }
836 void
837 nsScriptNameSpaceManager::EnumerateGlobalNames(NameEnumerator aEnumerator,
838 void* aClosure)
839 {
840 NameClosure closure = { aEnumerator, aClosure };
841 PL_DHashTableEnumerate(&mGlobalNames, EnumerateName, &closure);
842 }
844 void
845 nsScriptNameSpaceManager::EnumerateNavigatorNames(NameEnumerator aEnumerator,
846 void* aClosure)
847 {
848 NameClosure closure = { aEnumerator, aClosure };
849 PL_DHashTableEnumerate(&mNavigatorNames, EnumerateName, &closure);
850 }
852 static size_t
853 SizeOfEntryExcludingThis(PLDHashEntryHdr *aHdr, MallocSizeOf aMallocSizeOf,
854 void *aArg)
855 {
856 GlobalNameMapEntry* entry = static_cast<GlobalNameMapEntry*>(aHdr);
857 return entry->SizeOfExcludingThis(aMallocSizeOf);
858 }
860 MOZ_DEFINE_MALLOC_SIZE_OF(ScriptNameSpaceManagerMallocSizeOf)
862 NS_IMETHODIMP
863 nsScriptNameSpaceManager::CollectReports(
864 nsIHandleReportCallback* aHandleReport, nsISupports* aData)
865 {
866 return MOZ_COLLECT_REPORT(
867 "explicit/script-namespace-manager", KIND_HEAP, UNITS_BYTES,
868 SizeOfIncludingThis(ScriptNameSpaceManagerMallocSizeOf),
869 "Memory used for the script namespace manager.");
870 }
872 size_t
873 nsScriptNameSpaceManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
874 {
875 size_t n = 0;
876 n += PL_DHashTableSizeOfExcludingThis(&mGlobalNames,
877 SizeOfEntryExcludingThis, aMallocSizeOf);
878 n += PL_DHashTableSizeOfExcludingThis(&mNavigatorNames,
879 SizeOfEntryExcludingThis, aMallocSizeOf);
880 return n;
881 }