Bug 514661 - Introduce scoped atom tables for the HTML5 parser. r=bsmedberg, sr=dbaron.
authorHenri Sivonen <hsivonen@iki.fi>
Fri, 18 Sep 2009 18:13:10 +0300
changeset 34937 bec299562b229fd0fe371be0aa805182f653a8e6
parent 34936 5f884eb3f6b7ebd5b0fdc4ebc477ef1c8abe4633
child 34938 37f7608bae721ab6541235dac3d26f659656794c
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg, dbaron
bugs514661
milestone1.9.3a1pre
Bug 514661 - Introduce scoped atom tables for the HTML5 parser. r=bsmedberg, sr=dbaron.
layout/build/nsLayoutStatics.cpp
parser/html/Makefile.in
parser/html/nsHtml5Atom.cpp
parser/html/nsHtml5Atom.h
parser/html/nsHtml5AtomTable.cpp
parser/html/nsHtml5AtomTable.h
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsIAtom.idl
xpcom/tests/Makefile.in
xpcom/tests/MoreTestingAtomList.h
xpcom/tests/MoreTestingAtoms.cpp
xpcom/tests/MoreTestingAtoms.h
xpcom/tests/TestStaticAtoms.cpp
xpcom/tests/TestingAtomList.h
xpcom/tests/TestingAtoms.cpp
xpcom/tests/TestingAtoms.h
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -283,16 +283,18 @@ nsLayoutStatics::Initialize()
   nsCrossSiteListenerProxy::Startup();
 
   rv = nsFrameList::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsFrameList");
     return rv;
   }
 
+  NS_SealStaticAtomTable();
+
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
   nsFocusManager::Shutdown();
 #ifdef MOZ_XUL
--- a/parser/html/Makefile.in
+++ b/parser/html/Makefile.in
@@ -47,16 +47,18 @@ LIBXUL_LIBRARY	= 1
 
 
 EXPORTS		= \
 		nsHtml5Module.h \
 		$(NULL)
 
 CPPSRCS		= \
 		nsHtml5Atoms.cpp \
+		nsHtml5Atom.cpp \
+		nsHtml5AtomTable.cpp \
 		nsHtml5Parser.cpp \
 		nsHtml5AttributeName.cpp \
 		nsHtml5ElementName.cpp \
 		nsHtml5HtmlAttributes.cpp \
 		nsHtml5StackNode.cpp \
 		nsHtml5UTF16Buffer.cpp \
 		nsHtml5NamedCharacters.cpp \
 		nsHtml5Tokenizer.cpp \
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5Atom.cpp
@@ -0,0 +1,109 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsHtml5Atom.h"
+
+nsHtml5Atom::nsHtml5Atom(const nsAString& aString)
+  : mData(aString)
+{
+}
+
+nsHtml5Atom::~nsHtml5Atom()
+{
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsHtml5Atom::AddRef()
+{
+  NS_NOTREACHED("Attempt to AddRef an nsHtml5Atom.");
+  return 2;
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+nsHtml5Atom::Release()
+{
+  NS_NOTREACHED("Attempt to Release an nsHtml5Atom.");
+  return 1;
+}
+
+NS_IMETHODIMP
+nsHtml5Atom::QueryInterface(REFNSIID aIID, void** aInstancePtr)
+{
+  NS_NOTREACHED("Attempt to call QueryInterface an nsHtml5Atom.");
+  return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+nsHtml5Atom::ToString(nsAString& aReturn)
+{
+  aReturn.Assign(mData);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHtml5Atom::ToUTF8String(nsACString& aReturn)
+{
+  NS_NOTREACHED("Should not attempt to convert to an UTF-8 string.");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsHtml5Atom::GetUTF8String(const char **aReturn)
+{
+  NS_NOTREACHED("Should not attempt to get a UTF-8 string from nsHtml5Atom");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHtml5Atom::IsStaticAtom()
+{
+  return PR_FALSE;
+}
+
+NS_IMETHODIMP
+nsHtml5Atom::Equals(const nsAString& aString, PRBool *aReturn)
+{
+  *aReturn = mData.Equals(aString);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHtml5Atom::EqualsUTF8(const nsACString& aString, PRBool *aReturn)
+{
+  NS_NOTREACHED("Should not attempt to compare with an UTF-8 string.");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5Atom.h
@@ -0,0 +1,63 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsHtml5Atom_h_
+#define nsHtml5Atom_h_
+
+#include "nsIAtom.h"
+
+/**
+ * A dynamic atom implementation meant for use within the nsHtml5Tokenizer and 
+ * nsHtml5TreeBuilder owned by one nsHtml5Parser or nsHtml5StreamParser 
+ * instance.
+ *
+ * Usage is documented in nsHtml5AtomTable and nsIAtom.
+ */
+class nsHtml5Atom : public nsIAtom
+{
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIATOM
+
+    nsHtml5Atom(const nsAString& aString);
+    ~nsHtml5Atom();
+
+  private:
+    nsString mData;
+};
+
+#endif // nsHtml5Atom_h_
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5AtomTable.cpp
@@ -0,0 +1,89 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsHtml5AtomTable.h"
+#include "nsHtml5Atom.h"
+#include "nsThreadUtils.h"
+
+nsHtml5AtomEntry::nsHtml5AtomEntry(KeyTypePointer aStr)
+  : nsStringHashKey(aStr)
+  , mAtom(new nsHtml5Atom(*aStr))
+{
+}
+
+nsHtml5AtomEntry::nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther)
+  : nsStringHashKey(aOther)
+  , mAtom(nsnull)
+{
+  NS_NOTREACHED("nsHtml5AtomTable is broken and tried to copy an entry");
+}
+
+nsHtml5AtomEntry::~nsHtml5AtomEntry()
+{
+}
+
+nsHtml5AtomTable::nsHtml5AtomTable()
+{
+#ifdef DEBUG
+  NS_GetMainThread(getter_AddRefs(mPermittedLookupThread));
+#endif
+}
+
+nsHtml5AtomTable::~nsHtml5AtomTable()
+{
+}
+
+nsIAtom*
+nsHtml5AtomTable::GetAtom(const nsAString& aKey)
+{
+#ifdef DEBUG
+  {
+    nsCOMPtr<nsIThread> currentThread;
+    NS_GetCurrentThread(getter_AddRefs(currentThread));
+    NS_ASSERTION(mPermittedLookupThread == currentThread, "Wrong thread!");
+  }
+#endif
+  nsIAtom* atom = NS_GetStaticAtom(aKey);
+  if (atom) {
+    return atom;
+  }
+  nsHtml5AtomEntry* entry = mTable.PutEntry(aKey);
+  if (!entry) {
+    return nsnull;
+  }
+  return entry->GetAtom();
+}
new file mode 100644
--- /dev/null
+++ b/parser/html/nsHtml5AtomTable.h
@@ -0,0 +1,145 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsHtml5AtomTable_h_
+#define nsHtml5AtomTable_h_
+
+#include "nsHashKeys.h"
+#include "nsTHashtable.h"
+#include "nsAutoPtr.h"
+#include "nsIAtom.h"
+#include "nsIThread.h"
+
+class nsHtml5Atom;
+
+class nsHtml5AtomEntry : public nsStringHashKey
+{
+  public:
+    nsHtml5AtomEntry(KeyTypePointer aStr);
+    nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther);
+    ~nsHtml5AtomEntry();
+    inline nsHtml5Atom* GetAtom() {
+      return mAtom;
+    }
+  private:
+    nsAutoPtr<nsHtml5Atom> mAtom;
+};
+
+/**
+ * nsHtml5AtomTable provides non-locking lookup and creation of atoms for 
+ * nsHtml5Parser or nsHtml5StreamParser.
+ *
+ * The hashtable holds dynamically allocated atoms that are private to an 
+ * instance of nsHtml5Parser or nsHtml5StreamParser. (Static atoms are used on 
+ * interned nsHtml5ElementNames and interned nsHtml5AttributeNames. Also, when 
+ * the doctype name is 'html', that identifier needs to be represented as a 
+ * static atom.)
+ *
+ * Each instance of nsHtml5Parser has a single instance of nsHtml5AtomTable, 
+ * and each instance of nsHtml5StreamParser has a single instance of 
+ * nsHtml5AtomTable. Dynamic atoms obtained from an nsHtml5AtomTable are valid 
+ * for == comparison with each other or with atoms declared in nsHtml5Atoms 
+ * within the nsHtml5Tokenizer and the nsHtml5TreeBuilder instances owned by 
+ * the same nsHtml5Parser/nsHtml5StreamParser instance that owns the 
+ * nsHtml5AtomTable instance.
+ * 
+ * Dynamic atoms (atoms whose IsStaticAtom() returns PR_FALSE) obtained from 
+ * nsHtml5AtomTable must be re-obtained from another atom table when there's a 
+ * need to migrate atoms from an nsHtml5Parser to its nsHtml5StreamParser 
+ * (re-obtain from the other nsHtml5AtomTable), from an nsHtml5Parser to its 
+ * owner nsHtml5Parser (re-obtain from the other nsHtml5AtomTable) or from the 
+ * parser to the DOM (re-obtain from the application-wide atom table). To 
+ * re-obtain an atom from another atom table, obtain a string from the atom 
+ * using ToString(nsAString&) and look up an atom in the other table using that 
+ * string.
+ *
+ * An instance of nsHtml5AtomTable that belongs to an nsHtml5Parser is only 
+ * accessed from the main thread. An instance of nsHtml5AtomTable that belongs 
+ * to an nsHtml5StreamParser is accessed both from the main thread and from the 
+ * thread that executes the runnables of the nsHtml5StreamParser instance. 
+ * However, the threads never access the nsHtml5AtomTable instance concurrently 
+ * in the nsHtml5StreamParser case.
+ *
+ * Methods on the atoms obtained from nsHtml5AtomTable may be called on any 
+ * thread, although they only need to be called on the main thread or on the 
+ * thread working for the nsHtml5StreamParser when nsHtml5AtomTable belongs to 
+ * an nsHtml5StreamParser.
+ *
+ * Dynamic atoms obtained from nsHtml5AtomTable are deleted when the 
+ * nsHtml5AtomTable itself is destructed, which happens when the owner 
+ * nsHtml5Parser or nsHtml5StreamParser is destructed.
+ */
+class nsHtml5AtomTable
+{
+  public:
+    nsHtml5AtomTable();
+    ~nsHtml5AtomTable();
+    
+    /**
+     * Must be called after the constructor before use. Returns PR_TRUE
+     * when successful and PR_FALSE on OOM failure.
+     */
+    inline PRBool Init() {
+      return mTable.Init();
+    }
+    
+    /**
+     * Obtains the atom for the given string in the scope of this atom table.
+     */
+    nsIAtom* GetAtom(const nsAString& aKey);
+    
+    /**
+     * Empties the table.
+     */
+    void Clear() {
+      mTable.Clear();
+    }
+    
+#ifdef DEBUG
+    void SetPermittedLookupThread(nsIThread* aThread) {
+      mPermittedLookupThread = aThread;
+    }
+#endif  
+  
+  private:
+    nsTHashtable<nsHtml5AtomEntry> mTable;
+#ifdef DEBUG
+    nsCOMPtr<nsIThread>            mPermittedLookupThread;
+#endif
+};
+
+#endif // nsHtml5AtomTable_h_
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -40,16 +40,18 @@
 #include "nsStaticAtom.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsUTF8Utils.h"
 #include "nsCRT.h"
 #include "pldhash.h"
 #include "prenv.h"
 #include "nsThreadUtils.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
 
 #define PL_ARENA_CONST_ALIGN_MASK 3
 #include "plarena.h"
 
 class nsStaticAtomWrapper;
 
 /**
  * The shared hash table for atom lookups.
@@ -61,16 +63,27 @@ class nsStaticAtomWrapper;
  * If |gAtomTable.ops| is 0, then the table is uninitialized.
  */
 static PLDHashTable gAtomTable;
 
 // this is where we keep the nsStaticAtomWrapper objects
 
 static PLArenaPool* gStaticAtomArena = 0;
 
+/**
+ * A hashtable of static atoms that existed at app startup. This hashtable helps 
+ * nsHtml5AtomTable.
+ */
+static nsDataHashtable<nsStringHashKey, nsIAtom*>* gStaticAtomTable = 0;
+
+/**
+ * Whether it is still OK to add atoms to gStaticAtomTable.
+ */
+static PRBool gStaticAtomTableSealed = PR_FALSE;
+
 class nsStaticAtomWrapper : public nsIAtom
 {
 public:
   nsStaticAtomWrapper(const nsStaticAtom* aAtom, PRUint32 aLength) :
     mStaticAtom(aAtom), mLength(aLength)
   {
     MOZ_COUNT_CTOR(nsStaticAtomWrapper);
   }
@@ -396,16 +409,18 @@ void PromoteToPermanent(AtomImpl* aAtom)
   }
 #endif
   aAtom = new (aAtom) PermanentAtomImpl();
 }
 
 void
 NS_PurgeAtomTable()
 {
+  delete gStaticAtomTable;
+
   if (gAtomTable.ops) {
 #ifdef DEBUG
     const char *dumpAtomLeaks = PR_GetEnv("MOZ_DUMP_ATOM_LEAKS");
     if (dumpAtomLeaks && *dumpAtomLeaks) {
       PRUint32 leaked = 0;
       printf("*** %d atoms still exist (including permanent):\n",
              gAtomTable.entryCount);
       PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked);
@@ -544,16 +559,22 @@ AtomImpl::EqualsUTF8(const nsACString& a
 NS_IMETHODIMP
 AtomImpl::Equals(const nsAString& aString, PRBool* aResult)
 {
   *aResult = CompareUTF8toUTF16(nsDependentCString(mString, mLength),
                                 aString) == 0;
   return NS_OK;
 }
 
+NS_IMETHODIMP_(PRBool)
+AtomImpl::IsStaticAtom()
+{
+  return PR_FALSE;
+}
+
 //----------------------------------------------------------------------
 
 // wrapper class for the nsStaticAtom struct
 
 NS_IMETHODIMP_(nsrefcnt)
 nsStaticAtomWrapper::AddRef()
 {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
@@ -604,16 +625,23 @@ nsStaticAtomWrapper::EqualsUTF8(const ns
 NS_IMETHODIMP
 nsStaticAtomWrapper::Equals(const nsAString& aString, PRBool* aResult)
 {
   *aResult = CompareUTF8toUTF16(nsDependentCString(mStaticAtom->mString,
                                                    mLength),
                                 aString) == 0;
   return NS_OK;
 }
+
+NS_IMETHODIMP_(PRBool)
+nsStaticAtomWrapper::IsStaticAtom()
+{
+  return PR_TRUE;
+}
+
 //----------------------------------------------------------------------
 
 static nsStaticAtomWrapper*
 WrapStaticAtom(const nsStaticAtom* aAtom, PRUint32 aLength)
 {
   if (!gStaticAtomArena) {
     gStaticAtomArena = new PLArenaPool;
     if (!gStaticAtomArena)
@@ -663,19 +691,29 @@ GetAtomHashEntry(const PRUnichar* aStrin
   AtomTableEntry key(aString, aLength);
   return static_cast<AtomTableEntry*>
                     (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
 }
 
 NS_COM nsresult
 NS_RegisterStaticAtoms(const nsStaticAtom* aAtoms, PRUint32 aAtomCount)
 {
-  // this does two things:
+  // this does three things:
   // 1) wraps each static atom in a wrapper, if necessary
   // 2) initializes the address pointed to by each mBits slot
+  // 3) puts the atom into the static atom table as well
+  
+  if (!gStaticAtomTable && !gStaticAtomTableSealed) {
+    gStaticAtomTable = new nsDataHashtable<nsStringHashKey, nsIAtom*>();
+    if (!gStaticAtomTable || !gStaticAtomTable->Init()) {
+      delete gStaticAtomTable;
+      gStaticAtomTable = nsnull;
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
   
   for (PRUint32 i=0; i<aAtomCount; i++) {
     NS_ASSERTION(nsCRT::IsAscii(aAtoms[i].mString),
                  "Static atoms must be ASCII!");
 
     PRUint32 stringLen = strlen(aAtoms[i].mString);
 
     AtomTableEntry *he =
@@ -699,16 +737,22 @@ NS_RegisterStaticAtoms(const nsStaticAto
     else {
       nsStaticAtomWrapper* atom = WrapStaticAtom(&aAtoms[i], stringLen);
       NS_ASSERTION(atom, "Failed to wrap static atom");
 
       // but even if atom is null, no real difference in code..
       he->SetStaticAtomWrapper(atom);
       if (aAtoms[i].mAtom)
         *aAtoms[i].mAtom = atom;
+        
+      if (!gStaticAtomTableSealed) {
+        nsAutoString key;
+        atom->ToString(key);
+        gStaticAtomTable->Put(key, atom);
+      }
     }
   }
   return NS_OK;
 }
 
 NS_COM nsIAtom*
 NS_NewAtom(const char* aUTF8String)
 {
@@ -822,8 +866,25 @@ NS_NewPermanentAtom(const PRUnichar* aUT
 }
 
 NS_COM nsrefcnt
 NS_GetNumberOfAtoms(void)
 {
   return gAtomTable.entryCount;
 }
 
+NS_COM nsIAtom*
+NS_GetStaticAtom(const nsAString& aUTF16String)
+{
+  NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
+  NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
+  nsIAtom* atom;
+  if (!gStaticAtomTable->Get(aUTF16String, &atom)) {
+    atom = nsnull;
+  }
+  return atom;
+}
+
+NS_COM void
+NS_SealStaticAtomTable()
+{
+  gStaticAtomTableSealed = PR_TRUE;
+}
--- a/xpcom/ds/nsIAtom.idl
+++ b/xpcom/ds/nsIAtom.idl
@@ -43,17 +43,17 @@
 %}
 
 /*
  * Should this really be scriptable?  Using atoms from script or proxies
  * could be dangerous since double-wrapping could lead to loss of
  * pointer identity.
  */
  
-[scriptable, uuid(3d1b15b0-93b4-11d1-895b-006008911b81)]
+[scriptable, uuid(fbffe332-0856-421a-9b83-aaed0081fc40)]
 interface nsIAtom : nsISupports
 {
   /**
    * Get the Unicode or UTF8 value for the string
    */
   AString toString(); 
   AUTF8String toUTF8String();
   
@@ -80,16 +80,20 @@ interface nsIAtom : nsISupports
 
   inline PRBool EqualsUTF8(const nsACString& s) {
     PRBool result;
     EqualsUTF8(s, &result);
     return result;
   }
 %}
   
+  /**
+   * Returns true if the atom is static and false otherwise.
+   */
+  [noscript, notxpcom] boolean isStaticAtom();
 };
 
 
 %{C++
 /*
  * The three forms of NS_NewAtom and do_GetAtom (for use with
  * |nsCOMPtr<nsIAtom>|) return the atom for the string given.  At any
  * given time there will always be one atom representing a given string.
@@ -153,9 +157,20 @@ inline already_AddRefed<nsIAtom> do_GetP
     { return NS_NewPermanentAtom(aUTF16String); }
 
 /**
  * Return a count of the total number of atoms currently
  * alive in the system.
  */
 extern NS_COM nsrefcnt NS_GetNumberOfAtoms(void);
 
+/**
+ * Return a pointer for a static atom for the string or null if there's 
+ * no static atom for this string.
+ */
+extern NS_COM nsIAtom* NS_GetStaticAtom(const nsAString& aUTF16String);
+
+/**
+ * Seal the static atom table
+ */
+extern NS_COM void NS_SealStaticAtomTable();
+
 %}
--- a/xpcom/tests/Makefile.in
+++ b/xpcom/tests/Makefile.in
@@ -108,16 +108,17 @@ CPP_UNIT_TESTS += \
                   TestStorageStream.cpp \
                   TestStrings.cpp \
                   TestSynchronization.cpp \
                   TestTArray.cpp \
                   TestThreadPool.cpp \
                   TestThreads.cpp \
                   TestTimeStamp.cpp \
                   TestXPIDLString.cpp \
+                  TestStaticAtoms.cpp \
                   $(NULL)
 endif
 
 ifdef MOZ_DEBUG
 # FIXME bug 523392: TestDeadlockDetector doesn't like Windows
 # FIXME bug 523378: also fails on OS X
 ifneq (,$(filter-out WINNT WINCE Darwin,$(OS_ARCH)))
 CPP_UNIT_TESTS += \
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/MoreTestingAtomList.h
@@ -0,0 +1,39 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+MORE_TESTING_ATOM(qux, "qux")
+MORE_TESTING_ATOM(quux, "quux")
new file mode 100755
--- /dev/null
+++ b/xpcom/tests/MoreTestingAtoms.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "MoreTestingAtoms.h"
+#include "nsStaticAtom.h"
+#include "nsMemory.h"
+
+// define storage for all atoms
+#define MORE_TESTING_ATOM(_name, _value) nsIAtom* MoreTestingAtoms::_name;
+#include "MoreTestingAtomList.h"
+#undef MORE_TESTING_ATOM
+
+static const nsStaticAtom MoreTestingAtoms_info[] = {
+
+#define MORE_TESTING_ATOM(name_, value_) { value_, &MoreTestingAtoms::name_ },
+#include "MoreTestingAtomList.h"
+#undef MORE_TESTING_ATOM
+};
+
+void MoreTestingAtoms::AddRefAtoms()
+{
+  NS_RegisterStaticAtoms(MoreTestingAtoms_info, 
+                         NS_ARRAY_LENGTH(MoreTestingAtoms_info));
+}
new file mode 100755
--- /dev/null
+++ b/xpcom/tests/MoreTestingAtoms.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+ 
+#ifndef MoreTestingAtoms_h_
+#define MoreTestingAtoms_h_
+
+#include "nsIAtom.h"
+
+class MoreTestingAtoms {
+  public:
+    static void AddRefAtoms();
+#define MORE_TESTING_ATOM(_name, _value) static nsIAtom* _name;
+#include "MoreTestingAtomList.h"
+#undef MORE_TESTING_ATOM
+};
+
+#endif /* MoreTestingAtoms_h_ */
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/TestStaticAtoms.cpp
@@ -0,0 +1,108 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "TestHarness.h"
+#include "TestingAtoms.cpp"
+#include "MoreTestingAtoms.cpp"
+
+int main(int argc, char** argv)
+{
+  ScopedXPCOM xpcom("TestStaticAtoms");
+  if (xpcom.failed()) {
+    return 1;
+  }
+
+  TestingAtoms::AddRefAtoms();
+
+  NS_SealStaticAtomTable();
+
+  nsCOMPtr<nsIAtom> atom = do_GetAtom("foo");
+  if (!atom) {
+    fail("Didn't get an atom for foo.");
+    return 1;
+  }
+  if (atom->IsStaticAtom()) {
+    passed("foo is a static atom");
+  } else {
+    fail("foo is not a static atom.");
+    return 1;
+  }
+  if (atom == TestingAtoms::foo) {
+    passed("foo is the right pointer");
+  } else {
+    fail("foo was not the right pointer");
+    return 1;
+  }
+  nsIAtom* staticAtom = NS_GetStaticAtom(NS_LITERAL_STRING("foo"));
+  if (!staticAtom) {
+    fail("Did not get a static atom for foo");
+    return 1;
+  }
+
+  if (atom == staticAtom) {
+    passed("do_GetAtom and NS_GetStaticAtom returned the same atom.");
+  } else {
+    fail("do_GetAtom and NS_GetStaticAtom returned different atoms.");
+    return 1;
+  }
+
+  MoreTestingAtoms::AddRefAtoms();
+  
+  atom = do_GetAtom("qux");
+  if (!atom) {
+    fail("Didn't get an atom for qux.");
+    return 1;
+  }
+  if (atom->IsStaticAtom()) {
+    passed("qux is a static atom");
+  } else {
+    fail("qux is not a static atom.");
+    return 1;
+  }
+  if (atom == MoreTestingAtoms::qux) {
+    passed("qux is the right pointer");
+  } else {
+    fail("qux was not the right pointer");
+    return 1;
+  }
+  staticAtom = NS_GetStaticAtom(NS_LITERAL_STRING("qux"));
+  if (staticAtom) {
+    fail("Got an atom for qux. The static atom table was not sealed properly.");
+    return 1;
+  }
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/TestingAtomList.h
@@ -0,0 +1,39 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is HTML Parser C++ Translator code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Henri Sivonen <hsivonen@iki.fi>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+TESTING_ATOM(foo, "foo")
+TESTING_ATOM(bar, "bar")
new file mode 100755
--- /dev/null
+++ b/xpcom/tests/TestingAtoms.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "TestingAtoms.h"
+#include "nsStaticAtom.h"
+#include "nsMemory.h"
+
+// define storage for all atoms
+#define TESTING_ATOM(_name, _value) nsIAtom* TestingAtoms::_name;
+#include "TestingAtomList.h"
+#undef TESTING_ATOM
+
+static const nsStaticAtom TestingAtoms_info[] = {
+
+#define TESTING_ATOM(name_, value_) { value_, &TestingAtoms::name_ },
+#include "TestingAtomList.h"
+#undef TESTING_ATOM
+};
+
+void TestingAtoms::AddRefAtoms()
+{
+  NS_RegisterStaticAtoms(TestingAtoms_info, NS_ARRAY_LENGTH(TestingAtoms_info));
+}
new file mode 100755
--- /dev/null
+++ b/xpcom/tests/TestingAtoms.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+ 
+#ifndef TestingAtoms_h_
+#define TestingAtoms_h_
+
+#include "nsIAtom.h"
+
+class TestingAtoms {
+  public:
+    static void AddRefAtoms();
+#define TESTING_ATOM(_name, _value) static nsIAtom* _name;
+#include "TestingAtomList.h"
+#undef TESTING_ATOM
+};
+
+#endif /* TestingAtoms_h_ */