Bug 481440. Make our id table always live. r=jst, sr=roc
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 23 Mar 2009 10:04:40 -0400
changeset 26468 8fddfa5d274853d30869e787f7bf6e4cb92ce86c
parent 26467 40f19b310ca5e336baf2eeeae1269e9cd4a8ca97
child 26469 ee14b862af9028a384cb8e3ed128d84d20bcb836
push id6088
push userbzbarsky@mozilla.com
push dateMon, 23 Mar 2009 14:05:44 +0000
treeherdermozilla-central@8fddfa5d2748 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, roc
bugs481440
milestone1.9.2a1pre
Bug 481440. Make our id table always live. r=jst, sr=roc
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/html/document/src/nsHTMLContentSink.cpp
content/html/document/src/nsHTMLDocument.cpp
content/html/document/test/Makefile.in
content/html/document/test/test_bug481440.html
content/xul/document/src/nsXULDocument.cpp
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -299,17 +299,16 @@ nsUint32ToContentHashEntry::VisitContent
   }
 
   nsIContent* v = GetContent();
   if (v) {
     aVisitor->Visit(v);
   }
 }
 
-#define ID_NOT_IN_DOCUMENT ((nsIContent *)2)
 #define NAME_NOT_VALID ((nsBaseContentList*)1)
 
 nsIdentifierMapEntry::~nsIdentifierMapEntry()
 {
   if (mNameContentList && mNameContentList != NAME_NOT_VALID) {
     NS_RELEASE(mNameContentList);
   }
 }
@@ -344,23 +343,19 @@ nsIdentifierMapEntry::CreateNameContentL
 {
   mNameContentList = new nsBaseContentList();
   NS_ENSURE_TRUE(mNameContentList, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(mNameContentList);
   return NS_OK;
 }
 
 nsIContent*
-nsIdentifierMapEntry::GetIdContent(PRBool* aNotInDocument)
-{
-  nsIContent* c = static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
-  if (aNotInDocument) {
-    *aNotInDocument = c == ID_NOT_IN_DOCUMENT;
-  }
-  return c != ID_NOT_IN_DOCUMENT ? c : nsnull;
+nsIdentifierMapEntry::GetIdContent()
+{
+  return static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
 }
 
 void
 nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
 {
   for (PRInt32 i = 0; i < mIdContentList.Count(); ++i) {
     aElements->AppendObject(static_cast<nsIContent*>(mIdContentList[i]));
   }
@@ -419,63 +414,63 @@ nsIdentifierMapEntry::FireChangeCallback
 }
 
 PRBool
 nsIdentifierMapEntry::AddIdContent(nsIContent* aContent)
 {
   NS_PRECONDITION(aContent, "Must have content");
   NS_PRECONDITION(mIdContentList.IndexOf(nsnull) < 0,
                   "Why is null in our list?");
-  NS_PRECONDITION(aContent != ID_NOT_IN_DOCUMENT,
-                  "Bogus content pointer");
-
-  nsIContent* currentContent = static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
-  if (currentContent == ID_NOT_IN_DOCUMENT) {
-    NS_ASSERTION(mIdContentList.Count() == 1, "Bogus count");
-    mIdContentList.ReplaceElementAt(aContent, 0);
-    FireChangeCallbacks(nsnull, aContent);
-    return PR_TRUE;
-  }
+
+#ifdef DEBUG
+  nsIContent* currentContent =
+    static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
+#endif
 
   // Common case
   if (mIdContentList.Count() == 0) {
     if (!mIdContentList.AppendElement(aContent))
       return PR_FALSE;
+    NS_ASSERTION(currentContent == nsnull, "How did that happen?");
     FireChangeCallbacks(nsnull, aContent);
     return PR_TRUE;
   }
 
-  // We seem to have multiple content nodes for the same id, or we're doing our
-  // top-down registration when the id table is going live.  Search for the
-  // right place to insert the content.
+  // We seem to have multiple content nodes for the same id, or XUL is messing
+  // with us.  Search for the right place to insert the content.
   PRInt32 start = 0;
   PRInt32 end = mIdContentList.Count();
   do {
     NS_ASSERTION(start < end, "Bogus start/end");
     
     PRInt32 cur = (start + end) / 2;
     NS_ASSERTION(cur >= start && cur < end, "What happened here?");
 
     nsIContent* curContent = static_cast<nsIContent*>(mIdContentList[cur]);
     if (curContent == aContent) {
       // Already in the list, so already in the right spot.  Get out of here.
+      // XXXbz this only happens because XUL does all sorts of random
+      // UpdateIdTableEntry calls.  Hate, hate, hate!
       return PR_TRUE;
     }
 
     if (nsContentUtils::PositionIsBefore(aContent, curContent)) {
       end = cur;
     } else {
       start = cur + 1;
     }
   } while (start != end);
 
   if (!mIdContentList.InsertElementAt(aContent, start))
     return PR_FALSE;
   if (start == 0) {
-    FireChangeCallbacks(currentContent, aContent);
+    nsIContent* oldContent =
+      static_cast<nsIContent*>(mIdContentList.SafeElementAt(1));
+    NS_ASSERTION(currentContent == oldContent, "How did that happen?");
+    FireChangeCallbacks(oldContent, aContent);
   }
   return PR_TRUE;
 }
 
 PRBool
 nsIdentifierMapEntry::RemoveIdContent(nsIContent* aContent)
 {
   // This should only be called while the document is in an update.
@@ -489,25 +484,16 @@ nsIdentifierMapEntry::RemoveIdContent(ns
   if (currentContent == aContent) {
     FireChangeCallbacks(currentContent,
                         static_cast<nsIContent*>(mIdContentList.SafeElementAt(0)));
   }
   return mIdContentList.Count() == 0 && !mNameContentList && !mChangeCallbacks;
 }
 
 void
-nsIdentifierMapEntry::FlagIDNotInDocument()
-{
-  NS_ASSERTION(mIdContentList.Count() == 0,
-               "Flagging ID not in document when we have content?");
-  // Note that if this fails that's OK; this is just an optimization
-  mIdContentList.AppendElement(ID_NOT_IN_DOCUMENT);
-}
-
-void
 nsIdentifierMapEntry::AddNameContent(nsIContent* aContent)
 {
   if (!mNameContentList || mNameContentList == NAME_NOT_VALID)
     return;
 
   // NOTE: this indexof is absolutely needed, since we don't flush
   // content notifications when we do document.foo resolution.  So
   // aContent may be in our list already and just now getting notified
@@ -2302,86 +2288,108 @@ nsDocument::RemoveFromNameTable(nsIConte
 
 void
 nsDocument::UpdateIdTableEntry(nsIContent *aContent)
 {
   nsIAtom* id = aContent->GetID();
   if (!id)
     return;
 
-  PRBool liveTable = IdTableIsLive();
-  nsIdentifierMapEntry *entry =
-    liveTable ? mIdentifierMap.PutEntry(id) : mIdentifierMap.GetEntry(id);
-
-  if (entry) {
+  nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
+
+  if (entry) { /* True except on OOM */
     entry->AddIdContent(aContent);
   }
 }
 
 void
 nsDocument::RemoveFromIdTable(nsIContent *aContent)
 {
   nsIAtom* id = aContent->GetID();
   if (!id)
     return;
 
   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
-  if (!entry)
+  if (!entry) /* Should be false unless we had OOM when adding the entry */
     return;
 
   if (entry->RemoveIdContent(aContent)) {
     mIdentifierMap.RemoveEntry(id);
   }
 }
 
 void
 nsDocument::UnregisterNamedItems(nsIContent *aContent)
 {
-  if (aContent->IsNodeOfType(nsINode::eTEXT)) {
-    // Text nodes are not named items nor can they have children.
+  if (!aContent->IsNodeOfType(nsINode::eELEMENT)) {
+    // non-element nodes are not named items nor can they have children.
     return;
   }
 
   RemoveFromNameTable(aContent);
   RemoveFromIdTable(aContent);
 
-  PRUint32 i, count = aContent->GetChildCount();
-  for (i = 0; i < count; ++i) {
-    UnregisterNamedItems(aContent->GetChildAt(i));
-  }
+#ifdef DEBUG
+  nsMutationGuard debugMutationGuard;
+#endif
+
+  PRUint32 count;
+  nsIContent * const * kidSlot = aContent->GetChildArray(&count);
+  nsIContent * const * end = kidSlot + count;
+  for (; kidSlot != end; ++kidSlot) {
+    UnregisterNamedItems(*kidSlot);
+  }
+
+  NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
 }
 
 void
 nsDocument::RegisterNamedItems(nsIContent *aContent)
 {
-  if (aContent->IsNodeOfType(nsINode::eTEXT)) {
-    // Text nodes are not named items nor can they have children.
+  if (!aContent->IsNodeOfType(nsINode::eELEMENT)) {
+    // non-element nodes are not named items nor can they have children.
     return;
   }
 
   UpdateNameTableEntry(aContent);
   UpdateIdTableEntry(aContent);
 
-  PRUint32 i, count = aContent->GetChildCount();
-  for (i = 0; i < count; ++i) {
-    RegisterNamedItems(aContent->GetChildAt(i));
-  }
+#ifdef DEBUG
+  nsMutationGuard debugMutationGuard;
+#endif
+
+  PRUint32 count;
+  nsIContent * const * kidSlot = aContent->GetChildArray(&count);
+  nsIContent * const * end = kidSlot + count;
+  for (; kidSlot != end; ++kidSlot) {
+    RegisterNamedItems(*kidSlot);
+  }
+
+  NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
 }
 
 void
 nsDocument::ContentAppended(nsIDocument* aDocument,
                             nsIContent* aContainer,
                             PRInt32 aNewIndexInContainer)
 {
   NS_ASSERTION(aDocument == this, "unexpected doc");
 
-  PRUint32 count = aContainer->GetChildCount();
-  for (PRUint32 i = aNewIndexInContainer; i < count; ++i) {
-    RegisterNamedItems(aContainer->GetChildAt(i));
-  }
+#ifdef DEBUG
+  nsMutationGuard debugMutationGuard;
+#endif
+
+  PRUint32 count;
+  nsIContent * const * kidSlot = aContainer->GetChildArray(&count);
+  nsIContent * const * end = kidSlot + count;
+  for (kidSlot += aNewIndexInContainer; kidSlot != end; ++kidSlot) {
+    RegisterNamedItems(*kidSlot);
+  }
+
+  NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
 }
 
 void
 nsDocument::ContentInserted(nsIDocument* aDocument,
                             nsIContent* aContainer,
                             nsIContent* aContent,
                             PRInt32 aIndexInContainer)
 {
@@ -3749,107 +3757,46 @@ nsDocument::CheckGetElementByIdArg(const
         EmptyString(), 0, 0,
         nsIScriptError::warningFlag,
         "DOM");
     return PR_FALSE;
   }
   return PR_TRUE;
 }
 
-static void
-MatchAllElementsId(nsIContent* aContent, nsIAtom* aId, nsIdentifierMapEntry* aEntry)
-{
-  if (aId == aContent->GetID()) {
-    aEntry->AddIdContent(aContent);
-  }
-
-  PRUint32 i, count = aContent->GetChildCount();
-  for (i = 0; i < count; i++) {
-    MatchAllElementsId(aContent->GetChildAt(i), aId, aEntry);
-  }
-}
-
 nsIdentifierMapEntry*
 nsDocument::GetElementByIdInternal(nsIAtom* aID)
 {
   // We don't have to flush before we do the initial hashtable lookup, since if
   // the id is already in the hashtable it couldn't have been removed without
   // us being notified (all removals notify immediately, as far as I can tell).
   // So do the lookup first.
   nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aID);
   NS_ENSURE_TRUE(entry, nsnull);
 
   if (entry->GetIdContent())
     return entry;
 
-  // Now we have to flush.  It could be that we have a cached "not in
-  // document" or know nothing about this ID yet but more content has been
-  // added to the document since.  Note that we have to flush notifications,
-  // so that the entry will get updated properly.
+  // Now we have to flush.  It could be that we know nothing about this ID yet
+  // but more content has been added to the document since.  Note that we have
+  // to flush notifications, so that the entry will get updated properly.
 
   // Make sure to stash away the current generation so we can check whether
   // the table changes when we flush.
   PRUint32 generation = mIdentifierMap.GetGeneration();
   
   FlushPendingNotifications(Flush_ContentAndNotify);
 
   if (generation != mIdentifierMap.GetGeneration()) {
     // Table changed, so the entry pointer is no longer valid; look up the
     // entry again, adding if necessary (the adding may be necessary in case
     // the flush actually deleted entries).
     entry = mIdentifierMap.PutEntry(aID);
   }
   
-  PRBool isNotInDocument;
-  nsIContent *e = entry->GetIdContent(&isNotInDocument);
-  if (e || isNotInDocument)
-    return entry;
-
-  // Status of this id is unknown, search document
-  nsIContent* root = GetRootContent();
-  if (!IdTableIsLive()) {
-    if (IdTableShouldBecomeLive()) {
-      // Just make sure our table is up to date and call this method again
-      // to look up in the hashtable.
-      if (root) {
-        RegisterNamedItems(root);
-      }
-      return GetElementByIdInternal(aID);
-    }
-
-    if (root) {
-      // No-one should have registered an ID change callback yet. We don't
-      // want to fire one as a side-effect of getElementById! This shouldn't
-      // happen, since if someone called AddIDTargetObserver already for
-      // this ID, we should have filled in this entry with content or
-      // not-in-document.
-      NS_ASSERTION(!entry->HasContentChangeCallback(),
-                   "No callbacks should be registered while we set up this entry");
-      MatchAllElementsId(root, aID, entry);
-      e = entry->GetIdContent();
-    }
-  }
-
-  if (!e) {
-#ifdef DEBUG
-    // No reason to call MatchElementId if !IdTableIsLive, since
-    // we'd have done just that already
-    if (IdTableIsLive() && root && aID != nsGkAtoms::_empty) {
-      nsIContent* eDebug =
-        nsContentUtils::MatchElementId(root, aID);
-      NS_ASSERTION(!eDebug,
-                   "We got null for |e| but MatchElementId found something?");
-    }
-#endif
-    // There is no element with the given id in the document, cache
-    // the fact that it's not in the document
-    entry->FlagIDNotInDocument();
-    return entry;
-  }
-
   return entry;
 }
 
 NS_IMETHODIMP
 nsDocument::GetElementById(const nsAString& aElementId,
                            nsIDOMElement** aReturn)
 {
   NS_ENSURE_ARG_POINTER(aReturn);
@@ -3858,20 +3805,18 @@ nsDocument::GetElementById(const nsAStri
   nsCOMPtr<nsIAtom> idAtom(do_GetAtom(aElementId));
   NS_ENSURE_TRUE(idAtom, NS_ERROR_OUT_OF_MEMORY);
   if (!CheckGetElementByIdArg(idAtom))
     return NS_OK;
 
   nsIdentifierMapEntry *entry = GetElementByIdInternal(idAtom);
   NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
 
-  PRBool isNotInDocument;
-  nsIContent *e = entry->GetIdContent(&isNotInDocument);
-  NS_ASSERTION(e || isNotInDocument, "Incomplete map entry!");
-  if (isNotInDocument)
+  nsIContent *e = entry->GetIdContent();
+  if (!e)
     return NS_OK;
 
   return CallQueryInterface(e, aReturn);
 }
 
 nsIContent*
 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
                                 void* aData)
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -252,33 +252,33 @@ public:
   nsBaseContentList* GetNameContentList() {
     return mNameContentList;
   }
   nsresult CreateNameContentList();
 
   /**
    * Returns the element if we know the element associated with this
    * id. Otherwise returns null.
-   * @param aIsNotInDocument if non-null, we set the output to true
-   * if we know for sure the element is not in the document.
    */
-  nsIContent* GetIdContent(PRBool* aIsNotInDocument = nsnull);
+  nsIContent* GetIdContent();
+  /**
+   * Append all the elements with this id to aElements
+   */
   void AppendAllIdContent(nsCOMArray<nsIContent>* aElements);
   /**
    * This can fire ID change callbacks.
    * @return true if the content could be added, false if we failed due
    * to OOM.
    */
   PRBool AddIdContent(nsIContent* aContent);
   /**
    * This can fire ID change callbacks.
    * @return true if this map entry should be removed
    */
   PRBool RemoveIdContent(nsIContent* aContent);
-  void FlagIDNotInDocument();
 
   PRBool HasContentChangeCallback() { return mChangeCallbacks != nsnull; }
   void AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData);
   void RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData);
 
   void Traverse(nsCycleCollectionTraversalCallback* aCallback);
 
   void SetDocAllList(nsContentList* aContentList) { mDocAllList = aContentList; }
@@ -313,18 +313,17 @@ public:
     enum { ALLOW_MEMMOVE = PR_TRUE };
     
     ChangeCallback mKey;
   };
 
 private:
   void FireChangeCallbacks(nsIContent* aOldContent, nsIContent* aNewContent);
 
-  // The single element ID_NOT_IN_DOCUMENT, or empty to indicate we
-  // don't know what element(s) have this key as an ID
+  // empty if there are no nodes with this ID
   nsSmallVoidArray mIdContentList;
   // NAME_NOT_VALID if this id cannot be used as a 'name'
   nsBaseContentList *mNameContentList;
   nsRefPtr<nsContentList> mDocAllList;
   nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
 };
 
 class nsDocHeaderData
@@ -1144,18 +1143,18 @@ protected:
   nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
   nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   nsRefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
    *    entries as needed).
    * 2) Removals from the DOM affect the table immediately
-   * 3) Additions to the DOM always update existing entries, but only add new
-   *    ones if IdTableIsLive() is true.
+   * 3) Additions to the DOM always update existing entries for names, and add
+   *    new ones for IDs.
    */
   nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
 
   nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
 
   // True if the document has been detached from its content viewer.
   PRPackedBool mIsGoingAway:1;
   // True if our content viewer has been removed from the docshell
@@ -1187,32 +1186,16 @@ protected:
   PRPackedBool mHaveInputEncoding:1;
 
   PRPackedBool mInXBLUpdate:1;
 
   PRUint8 mXMLDeclarationBits;
 
   PRUint8 mDefaultElementType;
 
-  PRBool IdTableIsLive() const {
-    // live if we've had over 63 misses
-    return (mIdMissCount & 0x40) != 0;
-  }
-  void SetIdTableLive() {
-    mIdMissCount = 0x40;
-  }
-  PRBool IdTableShouldBecomeLive() {
-    NS_ASSERTION(!IdTableIsLive(),
-                 "Shouldn't be called if table is already live!");
-    ++mIdMissCount;
-    return IdTableIsLive();
-  }
-
-  PRUint8 mIdMissCount;
-
   nsInterfaceHashtable<nsVoidPtrHashKey, nsPIBoxObject> *mBoxObjectTable;
 
   // The channel that got passed to StartDocumentLoad(), if any
   nsCOMPtr<nsIChannel> mChannel;
   nsRefPtr<nsHTMLStyleSheet> mAttrStyleSheet;
   nsCOMPtr<nsIHTMLCSSStyleSheet> mStyleAttrStyleSheet;
   nsRefPtr<nsXMLEventsManager> mXMLEventsManager;
 
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -2323,17 +2323,17 @@ HTMLContentSink::OpenContainer(const nsI
 
   switch (aNode.GetNodeType()) {
     case eHTMLTag_frameset:
       rv = OpenFrameset(aNode);
       break;
     case eHTMLTag_head:
       rv = OpenHeadContext();
       if (NS_SUCCEEDED(rv)) {
-        rv = AddAttributes(aNode, mHead, PR_FALSE, mHaveSeenHead);
+        rv = AddAttributes(aNode, mHead, PR_TRUE, mHaveSeenHead);
         mHaveSeenHead = PR_TRUE;
       }
       break;
     case eHTMLTag_body:
       rv = OpenBody(aNode);
       break;
     case eHTMLTag_html:
       if (mRoot) {
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -3198,26 +3198,18 @@ DocAllResultMatch(nsIContent* aContent, 
 
 
 nsresult
 nsHTMLDocument::GetDocumentAllResult(const nsAString& aID, nsISupports** aResult)
 {
   *aResult = nsnull;
 
   nsCOMPtr<nsIAtom> id = do_GetAtom(aID);
-  nsIdentifierMapEntry *entry;
-  if (IdTableIsLive()) {
-    entry = mIdentifierMap.GetEntry(id);
-    // If we did a lookup and it failed, there are no items with this id
-    if (!entry)
-      return NS_OK;
-  } else {
-    entry = mIdentifierMap.PutEntry(id);
-    NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
-  }
+  nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
+  NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
 
   nsIContent* root = GetRootContent();
   if (!root) {
     return NS_OK;
   }
 
   nsRefPtr<nsContentList> docAllList = entry->GetDocAllList();
   if (!docAllList) {
--- a/content/html/document/test/Makefile.in
+++ b/content/html/document/test/Makefile.in
@@ -84,13 +84,14 @@ include $(topsrcdir)/config/rules.mk
 		test_bug446483.html \
 		bug446483-iframe.html \
 		test_bug448564.html \
 		bug448564-iframe-1.html \
 		bug448564-iframe-2.html \
 		bug448564-iframe-3.html \
 		bug448564-echo.sjs \
 		bug448564-submit.js \
+		test_bug481440.html \
 		test_bug482659.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/document/test/test_bug481440.html
@@ -0,0 +1,31 @@
+<!--Test must be in quirks mode for document.all to work-->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=481440
+-->
+<head>
+  <title>Test for Bug 481440</title>
+  <script type="application/javascript" src="/MochiKit/MochiKit.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=481440">Mozilla Bug 481440</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <input name="x" id="y">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 481440 **/
+// Do a bunch of getElementById calls to catch hashtables auto-going live
+for (var i = 0; i < 500; ++i) {
+  document.getElementById(i);
+}
+is(document.all["x"], document.getElementById("y"),
+   "Unexpected node");
+</script>
+</pre>
+</body>
+</html>
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -1943,17 +1943,16 @@ nsXULDocument::CloneNode(PRBool aDeep, n
 //----------------------------------------------------------------------
 //
 // Implementation methods
 //
 
 nsresult
 nsXULDocument::Init()
 {
-    SetIdTableLive();
     mRefMap.Init();
 
     nsresult rv = nsXMLDocument::Init();
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Create our command dispatcher and hook it up.
     mCommandDispatcher = new nsXULCommandDispatcher(this);
     NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY);