Fixing bug 641552. Add mechanism for dynamically registering properties on the navigator object in all pages. r=peterv
authorJohnny Stenback <jst@mozilla.com>
Tue, 06 Sep 2011 15:01:01 -0400
changeset 76594 7fbe71f2b56e9203b67dc0585395820970c03a70
parent 76593 1bba630bb3a099194fc758697db513942687c321
child 76595 09935ede3c77b175d1f8db7d1c764483d8ed6e4f
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewerspeterv
bugs641552
milestone9.0a1
Fixing bug 641552. Add mechanism for dynamically registering properties on the navigator object in all pages. r=peterv
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfo.h
dom/base/nsIScriptNameSpaceManager.h
dom/base/nsScriptNameSpaceManager.cpp
dom/base/nsScriptNameSpaceManager.h
dom/tests/mochitest/bugs/Makefile.in
dom/tests/mochitest/bugs/test_bug597809.html
dom/tests/mochitest/bugs/test_bug641552.html
testing/mochitest/specialpowers/content/specialpowers.js
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -660,17 +660,18 @@ static nsDOMClassInfoData sClassInfoData
                            (DOM_DEFAULT_SCRIPTABLE_FLAGS &
                             ~nsIXPCScriptable::ALLOW_PROP_MODS_TO_PROTOTYPE))
 
   NS_DEFINE_CLASSINFO_DATA(CaretPosition, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(Navigator, nsNavigatorSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS |
-                           nsIXPCScriptable::WANT_PRECREATE)
+                           nsIXPCScriptable::WANT_PRECREATE |
+                           nsIXPCScriptable::WANT_NEWRESOLVE)
   NS_DEFINE_CLASSINFO_DATA(Plugin, nsPluginSH,
                            ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(PluginArray, nsPluginArraySH,
                            ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MimeType, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MimeTypeArray, nsMimeTypeArraySH,
                            ARRAY_SCRIPTABLE_FLAGS)
@@ -2009,33 +2010,35 @@ CutPrefix(const char *aName) {
 
   return aName;
 }
 
 // static
 nsresult
 nsDOMClassInfo::RegisterClassName(PRInt32 aClassInfoID)
 {
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager =
+    nsJSRuntime::GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   nameSpaceManager->RegisterClassName(sClassInfoData[aClassInfoID].mName,
                                       aClassInfoID,
                                       sClassInfoData[aClassInfoID].mChromeOnly,
                                       sClassInfoData[aClassInfoID].mDisabled,
                                       &sClassInfoData[aClassInfoID].mNameUTF16);
 
   return NS_OK;
 }
 
 // static
 nsresult
 nsDOMClassInfo::RegisterClassProtos(PRInt32 aClassInfoID)
 {
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager =
+    nsJSRuntime::GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
   PRBool found_old;
 
   const nsIID *primary_iid = sClassInfoData[aClassInfoID].mProtoChainInterface;
 
   if (!primary_iid || primary_iid == &NS_GET_IID(nsISupports)) {
     return NS_OK;
   }
@@ -2077,17 +2080,18 @@ nsDOMClassInfo::RegisterClassProtos(PRIn
 
   return NS_OK;
 }
 
 // static
 nsresult
 nsDOMClassInfo::RegisterExternalClasses()
 {
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager =
+    nsJSRuntime::GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   nsCOMPtr<nsIComponentRegistrar> registrar;
   nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsICategoryManager> cm =
     do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
@@ -5526,17 +5530,18 @@ private:
     return nameStruct;
   }
 
   static nsresult GetNameStruct(const nsAString& aName,
                                 const nsGlobalNameStruct **aNameStruct)
   {
     *aNameStruct = nsnull;
 
-    nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+    nsScriptNameSpaceManager *nameSpaceManager =
+      nsJSRuntime::GetNameSpaceManager();
     if (!nameSpaceManager) {
       NS_ERROR("Can't get namespace manager.");
       return NS_ERROR_UNEXPECTED;
     }
 
     nameSpaceManager->LookupName(aName, aNameStruct);
 
     // Return NS_OK here, aName just isn't a DOM class but nothing failed.
@@ -5734,17 +5739,18 @@ nsDOMConstructor::HasInstance(nsIXPConne
   NS_ENSURE_TRUE(class_name_struct, NS_ERROR_FAILURE);
 
   if (name_struct == class_name_struct) {
     *bp = JS_TRUE;
 
     return NS_OK;
   }
 
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager =
+    nsJSRuntime::GetNameSpaceManager();
   NS_ASSERTION(nameSpaceManager, "Can't get namespace manager?");
 
   const nsIID *class_iid;
   if (class_name_struct->mType == nsGlobalNameStruct::eTypeInterface ||
       class_name_struct->mType == nsGlobalNameStruct::eTypeClassProto) {
     class_iid = &class_name_struct->mIID;
   } else if (class_name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
     class_iid =
@@ -6087,17 +6093,18 @@ ResolvePrototype(nsIXPConnect *aXPConnec
 
 // static
 nsresult
 nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
                           JSObject *obj, jsid id, PRBool *did_resolve)
 {
   *did_resolve = PR_FALSE;
 
-  nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
+  nsScriptNameSpaceManager *nameSpaceManager =
+    nsJSRuntime::GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   nsDependentJSString name(id);
 
   const nsGlobalNameStruct *name_struct = nsnull;
   const PRUnichar *class_name = nsnull;
 
   nameSpaceManager->LookupName(name, &name_struct, &class_name);
@@ -7015,16 +7022,87 @@ nsLocationSH::PreCreate(nsISupports *nat
     return NS_ERROR_UNEXPECTED;
   }
 
   *parentObj = sgo->GetGlobalJSObject();
   return NS_OK;
 }
 
 // DOM Navigator helper
+
+NS_IMETHODIMP
+nsNavigatorSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                          JSObject *obj, jsid id, PRUint32 flags,
+                          JSObject **objp, PRBool *_retval)
+{
+  if (!JSID_IS_STRING(id) || (flags & JSRESOLVE_ASSIGNING)) {
+    return NS_OK;
+  }
+
+  nsScriptNameSpaceManager *nameSpaceManager =
+    nsJSRuntime::GetNameSpaceManager();
+  NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
+
+  nsDependentJSString name(id);
+
+  const nsGlobalNameStruct *name_struct = nsnull;
+
+  nameSpaceManager->LookupNavigatorName(name, &name_struct);
+
+  if (!name_struct) {
+    return NS_OK;
+  }
+  NS_ASSERTION(name_struct->mType == nsGlobalNameStruct::eTypeNavigatorProperty,
+               "unexpected type");
+
+  nsresult rv = NS_OK;
+
+  nsCOMPtr<nsISupports> native(do_CreateInstance(name_struct->mCID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  jsval prop_val = JSVAL_VOID; // Property value.
+
+  nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native));
+
+  if (gpi) {
+    JSObject *global = JS_GetGlobalForObject(cx, obj);
+
+    nsISupports *globalNative = XPConnect()->GetNativeOfWrapper(cx, global);
+    nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(globalNative);
+
+    if (!window) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    rv = gpi->Init(window, &prop_val);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (JSVAL_IS_PRIMITIVE(prop_val)) {
+    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+    rv = WrapNative(cx, obj, native, PR_TRUE, &prop_val,
+                    getter_AddRefs(holder));
+
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (!JS_WrapValue(cx, &prop_val)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  JSBool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val, nsnull, nsnull,
+                                      JSPROP_ENUMERATE);
+
+  *_retval = PR_TRUE;
+  *objp = obj;
+
+  return ok ? NS_OK : NS_ERROR_FAILURE;
+}
+
+// static
 nsresult
 nsNavigatorSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
                          JSObject *globalObj, JSObject **parentObj)
 {
   // window.navigator is persisted across document transitions if
   // we're loading a page from the same origin. Because of that we
   // need to parent the navigator wrapper at the outer window to avoid
   // holding on to the inner window where the navigator was initially
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -476,16 +476,19 @@ protected:
 
   virtual ~nsNavigatorSH()
   {
   }
 
 public:
   NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
                        JSObject *globalObj, JSObject **parentObj);
+  NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                        JSObject *obj, jsid id, PRUint32 flags,
+                        JSObject **objp, PRBool *_retval);
 
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsNavigatorSH(aData);
   }
 };
 
 
--- a/dom/base/nsIScriptNameSpaceManager.h
+++ b/dom/base/nsIScriptNameSpaceManager.h
@@ -46,16 +46,19 @@
 
 #define JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY \
   "JavaScript-global-property"
 
 // a global property that is only accessible to privileged script 
 #define JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY \
   "JavaScript-global-privileged-property"
 
+#define JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY \
+  "JavaScript-navigator-property"
+
 #define JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY \
   "JavaScript-global-static-nameset"
 
 #define JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY \
   "JavaScript-global-dynamic-nameset"
 
 #define JAVASCRIPT_DOM_CLASS \
   "JavaScript-DOM-class"
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -143,28 +143,29 @@ nsScriptNameSpaceManager::nsScriptNameSp
   MOZ_COUNT_CTOR(nsScriptNameSpaceManager);
 }
 
 nsScriptNameSpaceManager::~nsScriptNameSpaceManager()
 {
   if (mIsInitialized) {
     // Destroy the hash
     PL_DHashTableFinish(&mGlobalNames);
+    PL_DHashTableFinish(&mNavigatorNames);
   }
   MOZ_COUNT_DTOR(nsScriptNameSpaceManager);
 }
 
 nsGlobalNameStruct *
-nsScriptNameSpaceManager::AddToHash(const char *aKey,
+nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const char *aKey,
                                     const PRUnichar **aClassName)
 {
   NS_ConvertASCIItoUTF16 key(aKey);
   GlobalNameMapEntry *entry =
     static_cast<GlobalNameMapEntry *>
-               (PL_DHashTableOperate(&mGlobalNames, &key, PL_DHASH_ADD));
+               (PL_DHashTableOperate(aTable, &key, PL_DHASH_ADD));
 
   if (!entry) {
     return nsnull;
   }
 
   if (aClassName) {
     *aClassName = entry->mKey.get();
   }
@@ -366,17 +367,17 @@ nsScriptNameSpaceManager::RegisterExtern
 
 nsresult
 nsScriptNameSpaceManager::RegisterInterface(const char* aIfName,
                                             const nsIID *aIfIID,
                                             PRBool* aFoundOld)
 {
   *aFoundOld = PR_FALSE;
 
-  nsGlobalNameStruct *s = AddToHash(aIfName);
+  nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aIfName);
   NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
   if (s->mType != nsGlobalNameStruct::eTypeNotInitialized) {
     *aFoundOld = PR_TRUE;
 
     return NS_OK;
   }
 
@@ -403,16 +404,25 @@ nsScriptNameSpaceManager::Init()
     GlobalNameHashInitEntry
   };
 
   mIsInitialized = PL_DHashTableInit(&mGlobalNames, &hash_table_ops, nsnull,
                                      sizeof(GlobalNameMapEntry), 
                                      GLOBALNAME_HASHTABLE_INITIAL_SIZE);
   NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY);
 
+  mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops, nsnull,
+                                     sizeof(GlobalNameMapEntry), 
+                                     GLOBALNAME_HASHTABLE_INITIAL_SIZE);
+  if (!mIsInitialized) {
+    PL_DHashTableFinish(&mGlobalNames);
+
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
   nsresult rv = NS_OK;
 
   rv = FillHashWithDOMInterfaces();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsICategoryManager> cm =
     do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -505,27 +515,46 @@ nsScriptNameSpaceManager::LookupName(con
       *aClassName = nsnull;
     }
   }
 
   return NS_OK;
 }
 
 nsresult
+nsScriptNameSpaceManager::LookupNavigatorName(const nsAString& aName,
+                                              const nsGlobalNameStruct **aNameStruct)
+{
+  GlobalNameMapEntry *entry =
+    static_cast<GlobalNameMapEntry *>
+               (PL_DHashTableOperate(&mNavigatorNames, &aName,
+                                     PL_DHASH_LOOKUP));
+
+  if (PL_DHASH_ENTRY_IS_BUSY(entry) &&
+      !((&entry->mGlobalName)->mDisabled)) {
+    *aNameStruct = &entry->mGlobalName;
+  } else {
+    *aNameStruct = nsnull;
+  }
+
+  return NS_OK;
+}
+
+nsresult
 nsScriptNameSpaceManager::RegisterClassName(const char *aClassName,
                                             PRInt32 aDOMClassInfoID,
                                             PRBool aPrivileged,
                                             PRBool aDisabled,
                                             const PRUnichar **aResult)
 {
   if (!nsCRT::IsAscii(aClassName)) {
     NS_ERROR("Trying to register a non-ASCII class name");
     return NS_OK;
   }
-  nsGlobalNameStruct *s = AddToHash(aClassName, aResult);
+  nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName, aResult);
   NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
   if (s->mType == nsGlobalNameStruct::eTypeClassConstructor) {
     return NS_OK;
   }
 
   // If a external constructor is already defined with aClassName we
   // won't overwrite it.
@@ -550,17 +579,17 @@ nsresult
 nsScriptNameSpaceManager::RegisterClassProto(const char *aClassName,
                                              const nsIID *aConstructorProtoIID,
                                              PRBool *aFoundOld)
 {
   NS_ENSURE_ARG_POINTER(aConstructorProtoIID);
 
   *aFoundOld = PR_FALSE;
 
-  nsGlobalNameStruct *s = AddToHash(aClassName);
+  nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName);
   NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
   if (s->mType != nsGlobalNameStruct::eTypeNotInitialized &&
       s->mType != nsGlobalNameStruct::eTypeInterface) {
     *aFoundOld = PR_TRUE;
 
     return NS_OK;
   }
@@ -570,17 +599,17 @@ nsScriptNameSpaceManager::RegisterClassP
 
   return NS_OK;
 }
 
 nsresult
 nsScriptNameSpaceManager::RegisterExternalClassName(const char *aClassName,
                                                     nsCID& aCID)
 {
-  nsGlobalNameStruct *s = AddToHash(aClassName);
+  nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aClassName);
   NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
   // If an external constructor is already defined with aClassName we
   // won't overwrite it.
 
   if (s->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
     return NS_OK;
   }
@@ -600,17 +629,17 @@ nsScriptNameSpaceManager::RegisterDOMCID
                                             nsDOMClassInfoExternalConstructorFnc aConstructorFptr,
                                             const nsIID *aProtoChainInterface,
                                             const nsIID **aInterfaces,
                                             PRUint32 aScriptableFlags,
                                             PRBool aHasClassInterface,
                                             const nsCID *aConstructorCID)
 {
   const PRUnichar* className;
-  nsGlobalNameStruct *s = AddToHash(aName, &className);
+  nsGlobalNameStruct *s = AddToHash(&mGlobalNames, aName, &className);
   NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
   // If an external constructor is already defined with aClassName we
   // won't overwrite it.
 
   if (s->mType == nsGlobalNameStruct::eTypeClassConstructor ||
       s->mType == nsGlobalNameStruct::eTypeExternalClassInfo) {
     return NS_OK;
@@ -652,16 +681,18 @@ nsScriptNameSpaceManager::AddCategoryEnt
   // Observe() but this way, we have only one place to update and this is
   // not performance sensitive.
   nsGlobalNameStruct::nametype type;
   if (strcmp(aCategory, JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY) == 0) {
     type = nsGlobalNameStruct::eTypeExternalConstructor;
   } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY) == 0 ||
              strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0) {
     type = nsGlobalNameStruct::eTypeProperty;
+  } else if (strcmp(aCategory, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY) == 0) {
+    type = nsGlobalNameStruct::eTypeNavigatorProperty;
   } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY) == 0) {
     type = nsGlobalNameStruct::eTypeStaticNameSet;
   } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY) == 0) {
     type = nsGlobalNameStruct::eTypeDynamicNameSet;
   } else {
     return NS_OK;
   }
 
@@ -699,17 +730,17 @@ nsScriptNameSpaceManager::AddCategoryEnt
   nsMemory::Free(cidPtr);
 
   if (type == nsGlobalNameStruct::eTypeExternalConstructor) {
     nsXPIDLCString constructorProto;
     rv = aCategoryManager->GetCategoryEntry(JAVASCRIPT_GLOBAL_CONSTRUCTOR_PROTO_ALIAS_CATEGORY,
                                             categoryEntry.get(),
                                             getter_Copies(constructorProto));
     if (NS_SUCCEEDED(rv)) {
-      nsGlobalNameStruct *s = AddToHash(categoryEntry.get());
+      nsGlobalNameStruct *s = AddToHash(&mGlobalNames, categoryEntry.get());
       NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
       if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {
         s->mAlias = new nsGlobalNameStruct::ConstructorAlias;
         s->mType = nsGlobalNameStruct::eTypeExternalConstructorAlias;
         s->mChromeOnly = PR_FALSE;
         s->mAlias->mCID = cid;
         AppendASCIItoUTF16(constructorProto, s->mAlias->mProtoName);
@@ -717,17 +748,24 @@ nsScriptNameSpaceManager::AddCategoryEnt
       } else {
         NS_WARNING("Global script name not overwritten!");
       }
 
       return NS_OK;
     }
   }
 
-  nsGlobalNameStruct *s = AddToHash(categoryEntry.get());
+  PLDHashTable *table;
+  if (type == nsGlobalNameStruct::eTypeNavigatorProperty) {
+    table = &mNavigatorNames;
+  } else {
+    table = &mGlobalNames;
+  }
+
+  nsGlobalNameStruct *s = AddToHash(table, categoryEntry.get());
   NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
   if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {
     s->mType = type;
     s->mCID = cid;
     s->mChromeOnly =
       strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0;
   } else {
--- a/dom/base/nsScriptNameSpaceManager.h
+++ b/dom/base/nsScriptNameSpaceManager.h
@@ -70,16 +70,17 @@ struct nsGlobalNameStruct
     nsString mProtoName;
     nsGlobalNameStruct* mProto;    
   };
 
   enum nametype {
     eTypeNotInitialized,
     eTypeInterface,
     eTypeProperty,
+    eTypeNavigatorProperty,
     eTypeExternalConstructor,
     eTypeStaticNameSet,
     eTypeDynamicNameSet,
     eTypeClassConstructor,
     eTypeClassProto,
     eTypeExternalClassInfoCreator,
     eTypeExternalClassInfo,
     eTypeExternalConstructorAlias
@@ -123,16 +124,22 @@ public:
   // Returns a nsGlobalNameStruct for aName, or null if one is not
   // found. The returned nsGlobalNameStruct is only guaranteed to be
   // valid until the next call to any of the methods in this class.
   // It also returns a pointer to the string buffer of the classname
   // in the nsGlobalNameStruct.
   nsresult LookupName(const nsAString& aName,
                       const nsGlobalNameStruct **aNameStruct,
                       const PRUnichar **aClassName = nsnull);
+  // Returns a nsGlobalNameStruct for the navigator property aName, or
+  // null if one is not found. The returned nsGlobalNameStruct is only
+  // guaranteed to be valid until the next call to any of the methods
+  // in this class.
+  nsresult LookupNavigatorName(const nsAString& aName,
+                               const nsGlobalNameStruct **aNameStruct);
 
   nsresult RegisterClassName(const char *aClassName,
                              PRInt32 aDOMClassInfoID,
                              PRBool aPrivileged,
                              PRBool aDisabled,
                              const PRUnichar **aResult);
 
   nsresult RegisterClassProto(const char *aClassName,
@@ -156,17 +163,17 @@ public:
 
   nsGlobalNameStruct* GetConstructorProto(const nsGlobalNameStruct* aStruct);
 
 protected:
   // Adds a new entry to the hash and returns the nsGlobalNameStruct
   // that aKey will be mapped to. If mType in the returned
   // nsGlobalNameStruct is != eTypeNotInitialized, an entry for aKey
   // already existed.
-  nsGlobalNameStruct *AddToHash(const char *aKey,
+  nsGlobalNameStruct *AddToHash(PLDHashTable *aTable, const char *aKey,
                                 const PRUnichar **aClassName = nsnull);
 
   nsresult FillHash(nsICategoryManager *aCategoryManager,
                     const char *aCategory);
   nsresult FillHashWithDOMInterfaces();
   nsresult RegisterInterface(const char* aIfName,
                              const nsIID *aIfIID,
                              PRBool* aFoundOld);
@@ -179,16 +186,15 @@ protected:
    * @aCategoryManager Instance of the category manager service.
    * @aCategory        Category where the entry comes from.
    * @aEntry           The entry that should be added.
    */
   nsresult AddCategoryEntryToHash(nsICategoryManager* aCategoryManager,
                                   const char* aCategory,
                                   nsISupports* aEntry);
 
-  // Inline PLDHashTable, init with PL_DHashTableInit() and delete
-  // with PL_DHashTableFinish().
   PLDHashTable mGlobalNames;
+  PLDHashTable mNavigatorNames;
 
   PRPackedBool mIsInitialized;
 };
 
 #endif /* nsScriptNameSpaceManager_h__ */
--- a/dom/tests/mochitest/bugs/Makefile.in
+++ b/dom/tests/mochitest/bugs/Makefile.in
@@ -132,16 +132,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug593174.html \
 		file_bug593174_1.html \
 		file_bug593174_2.html \
 		test_bug612267.html \
 		test_bug617296.html \
 		test_bug620947.html \
 		test_bug622361.html \
 		test_bug633133.html \
+		test_bug641552.html \
 		test_bug642026.html \
 		test_bug648465.html \
 		test_bug654137.html \
 		test_window_bar.html \
 		file_window_bar.html \
 		test_resize_move_windows.html \
 		test_devicemotion_multiple_listeners.html \
 		devicemotion_outer.html \
--- a/dom/tests/mochitest/bugs/test_bug597809.html
+++ b/dom/tests/mochitest/bugs/test_bug597809.html
@@ -13,24 +13,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 597809 **/
 
 SimpleTest.waitForExplicitFinish();
 
-netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-var cm = Components.classes["@mozilla.org/categorymanager;1"]
-                   .getService(Components.interfaces.nsICategoryManager);
-cm.addCategoryEntry("JavaScript-global-property", "testSNSM", "@mozilla.org/embedcomp/prompt-service;1",
+SpecialPowers.addCategoryEntry("JavaScript-global-property", "testSNSM", "@mozilla.org/embedcomp/prompt-service;1",
                     false, true);
 
 SimpleTest.executeSoon(function () {
-  ok(window.testSNSM, "testSNSM should returns an object");
+  ok(window.testSNSM, "testSNSM should return an object");
   SimpleTest.finish();
 });
 
 
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_bug641552.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=641552
+-->
+<head>
+  <title>Test for Bug 641552</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=641552">Mozilla Bug 641552</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 641552 **/
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.addCategoryEntry("JavaScript-global-property", "randomname", "@mozilla.org/embedcomp/prompt-service;1",
+                    false, true);
+
+SpecialPowers.addCategoryEntry("JavaScript-navigator-property", "randomname1", "@mozilla.org/embedcomp/prompt-service;1",
+                    false, true);
+
+SpecialPowers.addCategoryEntry("JavaScript-navigator-property", "randomname2", "@mozilla.org/embedcomp/prompt-service;1",
+                    false, true);
+
+SimpleTest.executeSoon(function () {
+  ok(window.randomname, "window.randomname should return an object");
+  is(typeof(window.navigator.randomname1), 'object', "navigator.randomname1 should return an object");
+  is(typeof(window.navigator.randomname2), 'object', "navigator.randomname1 should return an object");
+  SimpleTest.finish();
+});
+
+
+</script>
+</pre>
+</body>
+</html>
--- a/testing/mochitest/specialpowers/content/specialpowers.js
+++ b/testing/mochitest/specialpowers/content/specialpowers.js
@@ -396,16 +396,22 @@ SpecialPowers.prototype = {
 
   log: function(data) {
     this._mfl.log(data);
   },
 
   closeLogFile: function() {
     this._mfl.close();
   },
+
+  addCategoryEntry: function(category, entry, value, persists, replace) {
+    Cc["@mozilla.org/categorymanager;1"].
+      getService(Components.interfaces.nsICategoryManager).
+      addCategoryEntry(category, entry, value, persists, replace);
+  },
 };
 
 // Expose everything but internal APIs (starting with underscores) to
 // web content.
 SpecialPowers.prototype.__exposedProps__ = {};
 for each (i in Object.keys(SpecialPowers.prototype).filter(function(v) {return v.charAt(0) != "_";})) {
   SpecialPowers.prototype.__exposedProps__[i] = "r";
 }