Bug 564863: Speed up id/name handling by letting elements register/unregister themselves. r=smaug
authorJonas Sicking <jonas@sicking.cc>
Thu, 03 Jun 2010 18:09:20 -0700
changeset 43079 ce0bc496b725cca4a56e2d046d30f71b6c394608
parent 43078 69df59e99741365f71372a7557e6fe4dc0d1cb8d
child 43080 9065d777cf6215cba066c90915d1a759e5b45f22
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs564863
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 564863: Speed up id/name handling by letting elements register/unregister themselves. r=smaug
content/base/public/nsContentUtils.h
content/base/public/nsIContent.h
content/base/public/nsIDocument.h
content/base/public/nsINode.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsDocumentFragment.cpp
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericDOMDataNode.h
content/base/src/nsGenericElement.cpp
content/base/src/nsGenericElement.h
content/base/src/nsNodeUtils.cpp
content/base/src/nsStyledElement.cpp
content/base/src/nsStyledElement.h
content/base/test/Makefile.in
content/base/test/test_bug564863.xhtml
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLLabelElement.cpp
content/html/document/src/nsHTMLDocument.cpp
content/html/document/src/nsHTMLDocument.h
content/xml/content/src/nsXMLElement.cpp
content/xml/content/src/nsXMLElement.h
content/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULElement.h
content/xul/document/public/nsIXULDocument.h
content/xul/document/src/nsXULDocument.cpp
content/xul/document/src/nsXULDocument.h
content/xul/templates/src/nsXULContentBuilder.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1346,22 +1346,16 @@ public:
   static PRUint32 FilterDropEffect(PRUint32 aAction, PRUint32 aEffectAllowed);
 
   /**
    * Return true if aURI is a local file URI (i.e. file://).
    */
   static PRBool URIIsLocalFile(nsIURI *aURI);
 
   /**
-   * If aContent is an HTML element with a DOM level 0 'name', then
-   * return the name. Otherwise return null.
-   */
-  static nsIAtom* IsNamedItem(Element* aElement);
-
-  /**
    * Get the application manifest URI for this document.  The manifest URI
    * is specified in the manifest= attribute of the root element of the
    * document.
    *
    * @param aDocument The document that lists the manifest.
    * @param aURI The manifest URI.
    */
   static void GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI);
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -66,18 +66,18 @@ enum nsLinkState {
   eLinkState_Unknown    = 0,
   eLinkState_Unvisited  = 1,
   eLinkState_Visited    = 2,
   eLinkState_NotLink    = 3
 };
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID       \
-{ 0x9e3b1a15, 0x72d5, 0x4e4f, \
-  { 0x8f, 0x4b, 0x75, 0xde, 0x07, 0x9c, 0x16, 0xdc } }
+{ 0x1450010b, 0xcdca, 0x451c, \
+  { 0xba, 0xdc, 0x07, 0x90, 0x89, 0x7b, 0xce, 0xb8 } }
 
 /**
  * A node of content in a document's content model. This interface
  * is supported by all content objects.
  */
 class nsIContent : public nsINode {
 public:
 #ifdef MOZILLA_INTERNAL_API
@@ -773,17 +773,22 @@ public:
   // PRInt32.  We should really use PRUint32 instead.
   virtual PRInt32 IntrinsicState() const;
 
   /**
    * Get the ID of this content node (the atom corresponding to the
    * value of the null-namespace attribute whose name is given by
    * GetIDAttributeName().  This may be null if there is no ID.
    */
-  virtual nsIAtom* GetID() const = 0;
+  nsIAtom* GetID() const {
+    if (HasFlag(NODE_HAS_ID)) {
+      return DoGetID();
+    }
+    return nsnull;
+  }
 
   /**
    * Get the class list of this content node (this corresponds to the
    * value of the null-namespace attribute whose name is given by
    * GetClassAttributeName()).  This may be null if there are no
    * classes, but that's not guaranteed.
    */
   const nsAttrValue* GetClasses() const {
@@ -910,16 +915,23 @@ public:
                               nsAString& aNamespaceURI) const;
 
   nsIAtom* LookupPrefix(const nsAString& aNamespaceURI);
 
   PRBool IsEqual(nsIContent *aOther);
 
   virtual PRBool IsEqualNode(nsINode* aOther);
 
+protected:
+  /**
+   * Hook for implementing GetID.  This is guaranteed to only be
+   * called if the NODE_HAS_ID flag is set.
+   */
+  virtual nsIAtom* DoGetID() const = 0;
+
 private:
   /**
    * Hook for implementing GetClasses.  This is guaranteed to only be
    * called if the NODE_MAY_HAVE_CLASS flag is set.
    */
   virtual const nsAttrValue* DoGetClasses() const = 0;
 
   /**
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -111,18 +111,18 @@ class Loader;
 
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID      \
-{ 0xdf6c0752, 0xe780, 0x4576, \
-  { 0x95, 0x3c, 0x7e, 0xf1, 0xde, 0x9f, 0xd7, 0xf0 } }
+{ 0x3ee6a14b, 0x83b5, 0x4629, \
+  { 0x96, 0x9b, 0xe9, 0x84, 0x7c, 0x57, 0x24, 0x3c } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              (1 << 0)
@@ -665,16 +665,27 @@ public:
     return mRemovedFromDocShell ? GetInnerWindowInternal() : mWindow;
   }
 
   /**
    * Get the script loader for this document
    */ 
   virtual nsScriptLoader* ScriptLoader() = 0;
 
+  /**
+   * Add/Remove an element to the document's id and name hashes
+   */
+  virtual void AddToIdTable(mozilla::dom::Element* aElement, nsIAtom* aId) = 0;
+  virtual void RemoveFromIdTable(mozilla::dom::Element* aElement,
+                                 nsIAtom* aId) = 0;
+  virtual void AddToNameTable(mozilla::dom::Element* aElement,
+                              nsIAtom* aName) = 0;
+  virtual void RemoveFromNameTable(mozilla::dom::Element* aElement,
+                                   nsIAtom* aName) = 0;
+
   //----------------------------------------------------------------------
 
   // Document notification API's
 
   /**
    * Add a new observer of document change notifications. Whenever
    * content is changed, appended, inserted or removed the observers are
    * informed.  An observer that is already observing the document must
@@ -797,16 +808,20 @@ public:
   virtual void GetXMLDeclaration(nsAString& aVersion,
                                  nsAString& aEncoding,
                                  nsAString& Standalone) = 0;
 
   PRBool IsHTML() const
   {
     return mIsRegularHTML;
   }
+  PRBool IsXUL() const
+  {
+    return mIsXUL;
+  }
 
   virtual PRBool IsScriptEnabled() = 0;
 
   virtual nsresult AddXMLEventsContent(nsIContent * aXMLEventsElement) = 0;
 
   /**
    * Create an element with the specified name, prefix and namespace ID.
    * If aDocumentDefaultType is true we create an element of the default type
@@ -1364,18 +1379,17 @@ public:
   virtual void SetChangeScrollPosWhenScrollingToRef(PRBool aValue) = 0;
 
   /**
    * This method is similar to GetElementById() from nsIDOMDocument but it
    * returns a mozilla::dom::Element instead of a nsIDOMElement.
    * It prevents converting nsIDOMElement to mozill:dom::Element which is
    * already converted from mozilla::dom::Element.
    */
-  virtual mozilla::dom::Element* GetElementById(const nsAString& aElementId,
-                                                nsresult* aResult) = 0;
+  virtual mozilla::dom::Element* GetElementById(const nsAString& aElementId) = 0;
 
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
@@ -1457,16 +1471,17 @@ protected:
   // basically be true only for documents that exist in newly-opened windows or
   // documents created to satisfy a GetDocument() on a window when there's no
   // document in it.
   PRPackedBool mIsInitialDocumentInWindow;
 
   PRPackedBool mShellIsHidden;
 
   PRPackedBool mIsRegularHTML;
+  PRPackedBool mIsXUL;
 
   // True if we're loaded as data and therefor has any dangerous stuff, such
   // as scripts and plugins, disabled.
   PRPackedBool mLoadedAsData;
 
   // If true, whoever is creating the document has gotten it to the
   // point where it's safe to start layout on it.
   PRPackedBool mMayStartLayout;
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -112,19 +112,20 @@ enum {
   // in the document and therefore should get bindings attached.
   NODE_FORCE_XBL_BINDINGS =      0x00000040U,
 
   // Whether a binding manager may have a pointer to this
   NODE_MAY_BE_IN_BINDING_MNGR =  0x00000080U,
 
   NODE_IS_EDITABLE =             0x00000100U,
 
-  // Optimizations to quickly check whether element may have ID, class or style
-  // attributes. Not all element implementations may use these!
-  NODE_MAY_HAVE_ID =             0x00000200U,
+  // Set to true if the element has a non-empty id attribute. This can in rare
+  // cases lie for nsXMLElement, such as when the node has been moved between
+  // documents with different id mappings.
+  NODE_HAS_ID =                  0x00000200U,
   // For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the
   // node in fact has a class, but may be set even if it doesn't.
   NODE_MAY_HAVE_CLASS =          0x00000400U,
   NODE_MAY_HAVE_STYLE =          0x00000800U,
 
   NODE_IS_INSERTION_PARENT =     0x00001000U,
 
   // Node has an :empty or :-moz-only-whitespace selector
@@ -169,18 +170,21 @@ enum {
   NODE_DESCENDANTS_NEED_FRAMES = 0x00100000U,
 
   // Set if the node is an element.
   NODE_IS_ELEMENT              = 0x00200000U,
   
   // Set if the node has the accesskey attribute set.
   NODE_HAS_ACCESSKEY           = 0x00400000U,
 
+  // Set if the node has the accesskey attribute set.
+  NODE_HAS_NAME                = 0x00800000U,
+
   // Four bits for the script-type ID
-  NODE_SCRIPT_TYPE_OFFSET =               23,
+  NODE_SCRIPT_TYPE_OFFSET =               24,
 
   NODE_SCRIPT_TYPE_SIZE =                  4,
 
   // Remaining bits are node type specific.
   NODE_TYPE_SPECIFIC_BITS_OFFSET =
     NODE_SCRIPT_TYPE_OFFSET + NODE_SCRIPT_TYPE_SIZE
 };
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4891,45 +4891,16 @@ nsContentUtils::EqualsIgnoreASCIICase(co
 
 /* static */
 void
 nsAutoGCRoot::Shutdown()
 {
   NS_IF_RELEASE(sJSRuntimeService);
 }
 
-nsIAtom*
-nsContentUtils::IsNamedItem(Element* aElement)
-{
-  // Only the content types reflected in Level 0 with a NAME
-  // attribute are registered. Images, layers and forms always get
-  // reflected up to the document. Applets and embeds only go
-  // to the closest container (which could be a form).
-  nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aElement);
-  if (!elm) {
-    return nsnull;
-  }
-
-  nsIAtom* tag = elm->Tag();
-  if (tag != nsGkAtoms::img    &&
-      tag != nsGkAtoms::form   &&
-      tag != nsGkAtoms::applet &&
-      tag != nsGkAtoms::embed  &&
-      tag != nsGkAtoms::object) {
-    return nsnull;
-  }
-
-  const nsAttrValue* val = elm->GetParsedAttr(nsGkAtoms::name);
-  if (val && val->Type() == nsAttrValue::eAtom) {
-    return val->GetAtomValue();
-  }
-
-  return nsnull;
-}
-
 /* static */
 nsIInterfaceRequestor*
 nsContentUtils::GetSameOriginChecker()
 {
   if (!sSameOriginChecker) {
     sSameOriginChecker = new nsSameOriginChecker();
     NS_IF_ADDREF(sSameOriginChecker);
   }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1449,17 +1449,17 @@ nsDocument::~nsDocument()
   if (mSubDocuments) {
     PL_DHashTableDestroy(mSubDocuments);
 
     mSubDocuments = nsnull;
   }
 
   // Destroy link map now so we don't waste time removing
   // links one by one
-  DestroyLinkMap();
+  DestroyElementMaps();
 
   nsAutoScriptBlocker scriptBlocker;
 
   PRInt32 indx; // must be signed
   PRUint32 count = mChildren.ChildCount();
   for (indx = PRInt32(count) - 1; indx >= 0; --indx) {
     mChildren.ChildAt(indx)->UnbindFromTree();
     mChildren.RemoveChildAt(indx);
@@ -1878,34 +1878,32 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
 #ifdef PR_LOGGING
   if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
     nsCAutoString spec;
     aURI->GetSpec(spec);
     PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get());
   }
 #endif
 
-  mIdentifierMap.Clear();
-
   SetPrincipal(nsnull);
   mSecurityInfo = nsnull;
 
   mDocumentLoadGroup = nsnull;
 
   // Delete references to sub-documents and kill the subdocument map,
   // if any. It holds strong references
   if (mSubDocuments) {
     PL_DHashTableDestroy(mSubDocuments);
 
     mSubDocuments = nsnull;
   }
 
   // Destroy link map now so we don't waste time removing
   // links one by one
-  DestroyLinkMap();
+  DestroyElementMaps();
 
   PRUint32 count = mChildren.ChildCount();
   { // Scope for update
     MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, PR_TRUE);    
     for (PRInt32 i = PRInt32(count) - 1; i >= 0; i--) {
       nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
 
       if (nsINode::GetFirstChild() == content) {
@@ -2329,189 +2327,70 @@ nsDocument::GetLastModified(nsAString& a
     // (or even the current time), fall back to what NS4.x returned.
     aLastModified.Assign(NS_LITERAL_STRING("01/01/1970 00:00:00"));
   }
 
   return NS_OK;
 }
 
 void
-nsDocument::UpdateNameTableEntry(Element *aElement)
+nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
 {
   if (!mIsRegularHTML)
     return;
 
-  nsIAtom* name = nsContentUtils::IsNamedItem(aElement);
-  if (!name)
-    return;
-
-  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(name);
-  if (!entry) {
-    // We're not tracking the elements with this name
-    return;
-  }
-
-  entry->AddNameElement(aElement);
+  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
+
+  // entry is null if we're not tracking the elements with this name
+
+  if (entry) {
+    entry->AddNameElement(aElement);
+  }
 }
 
 void
-nsDocument::RemoveFromNameTable(Element *aElement)
-{
-  if (!mIsRegularHTML)
+nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
+{
+  // Speed up document teardown
+  if (!mIsRegularHTML || mIdentifierMap.Count() == 0)
     return;
 
-  nsIAtom* name = nsContentUtils::IsNamedItem(aElement);
-  if (!name)
+  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
+  if (!entry) // Should never be false unless we had OOM when adding the entry
     return;
 
-  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(name);
-  if (!entry) {
-    // We're not tracking the elements with this name
-    return;
-  }
-
   entry->RemoveNameElement(aElement);
 }
 
 void
-nsDocument::UpdateIdTableEntry(Element *aElement)
-{
-  nsIAtom* id = aElement->GetID();
-  if (!id)
-    return;
-
-  nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
+nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
+{
+  nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aId);
 
   if (entry) { /* True except on OOM */
     entry->AddIdElement(aElement);
   }
 }
 
 void
-nsDocument::RemoveFromIdTable(Element *aElement)
-{
-  nsIAtom* id = aElement->GetID();
-  if (!id)
+nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
+{
+  NS_ASSERTION(aId, "huhwhatnow?");
+
+  // Speed up document teardown
+  if (mIdentifierMap.Count() == 0) {
     return;
-
-  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
-  if (!entry) /* Should be false unless we had OOM when adding the entry */
+  }
+
+  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
+  if (!entry) // Can be null for XML elements with changing ids.
     return;
 
   if (entry->RemoveIdElement(aElement)) {
-    mIdentifierMap.RemoveEntry(id);
-  }
-}
-
-void
-nsDocument::UnregisterNamedItems(nsIContent *aContent)
-{
-  if (!aContent->IsElement()) {
-    // non-element nodes are not named items nor can they have children.
-    return;
-  }
-
-  RemoveFromNameTable(aContent->AsElement());
-  RemoveFromIdTable(aContent->AsElement());
-
-  for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
-    UnregisterNamedItems(iter);
-  }
-}
-
-void
-nsDocument::RegisterNamedItems(nsIContent *aContent)
-{
-  if (!aContent->IsElement()) {
-    // non-element nodes are not named items nor can they have children.
-    return;
-  }
-
-  UpdateNameTableEntry(aContent->AsElement());
-  UpdateIdTableEntry(aContent->AsElement());
-
-  for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
-    RegisterNamedItems(iter);
-  }
-}
-
-void
-nsDocument::ContentAppended(nsIDocument* aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aFirstNewContent,
-                            PRInt32 aNewIndexInContainer)
-{
-  NS_ASSERTION(aDocument == this, "unexpected doc");
-
-  for (nsINode::ChildIterator iter(aContainer, aNewIndexInContainer);
-       !iter.IsDone();
-       iter.Next()) {
-    RegisterNamedItems(iter);
-  }
-}
-
-void
-nsDocument::ContentInserted(nsIDocument* aDocument,
-                            nsIContent* aContainer,
-                            nsIContent* aContent,
-                            PRInt32 aIndexInContainer)
-{
-  NS_ASSERTION(aDocument == this, "unexpected doc");
-
-  NS_ABORT_IF_FALSE(aContent, "Null content!");
-
-  RegisterNamedItems(aContent);
-}
-
-void
-nsDocument::ContentRemoved(nsIDocument* aDocument,
-                           nsIContent* aContainer,
-                           nsIContent* aChild,
-                           PRInt32 aIndexInContainer)
-{
-  NS_ASSERTION(aDocument == this, "unexpected doc");
-
-  NS_ABORT_IF_FALSE(aChild, "Null content!");
-
-  UnregisterNamedItems(aChild);
-}
-
-void
-nsDocument::AttributeWillChange(nsIDocument* aDocument,
-                                nsIContent* aContent, PRInt32 aNameSpaceID,
-                                nsIAtom* aAttribute, PRInt32 aModType)
-{
-  NS_ABORT_IF_FALSE(aContent && aContent->IsElement(), "Null content!");
-  NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
-
-  if (aNameSpaceID != kNameSpaceID_None)
-    return;
-  if (aAttribute == nsGkAtoms::name) {
-    RemoveFromNameTable(aContent->AsElement());
-  } else if (aAttribute == aContent->GetIDAttributeName()) {
-    RemoveFromIdTable(aContent->AsElement());
-  }
-}
-
-void
-nsDocument::AttributeChanged(nsIDocument* aDocument,
-                             nsIContent* aContent, PRInt32 aNameSpaceID,
-                             nsIAtom* aAttribute, PRInt32 aModType)
-{
-  NS_ASSERTION(aDocument == this, "unexpected doc");
-
-  NS_ABORT_IF_FALSE(aContent && aContent->IsElement(), "Null content!");
-  NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
-
-  if (aNameSpaceID != kNameSpaceID_None)
-    return;
-  if (aAttribute == nsGkAtoms::name) {
-    UpdateNameTableEntry(aContent->AsElement());
-  } else if (aAttribute == aContent->GetIDAttributeName()) {
-    UpdateIdTableEntry(aContent->AsElement());
+    mIdentifierMap.RemoveEntry(aId);
   }
 }
 
 nsIPrincipal*
 nsDocument::GetPrincipal()
 {
   return NodePrincipal();
 }
@@ -3320,17 +3199,17 @@ nsDocument::RemoveChildAt(PRUint32 aInde
   NS_ASSERTION(aMutationEvent, "Someone tried to inhibit mutations on document child removal.");
   nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex);
   if (!oldKid) {
     return NS_OK;
   }
 
   if (oldKid->IsElement()) {
     // Destroy the link map up front before we mess with the child list.
-    DestroyLinkMap();
+    DestroyElementMaps();
   }
 
   nsresult rv =
     doRemoveChildAt(aIndex, aNotify, oldKid, mChildren, aMutationEvent);
   mCachedRootElement = nsnull;
   return rv;
 }
 
@@ -3854,118 +3733,70 @@ nsDocument::CheckGetElementByIdArg(const
         EmptyString(), 0, 0,
         nsIScriptError::warningFlag,
         "DOM");
     return PR_FALSE;
   }
   return PR_TRUE;
 }
 
-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->GetIdElement())
-    return entry;
-
-  // 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);
-  }
-  
-  return entry;
-}
-
 Element*
-nsDocument::GetElementById(const nsAString& aElementId, nsresult *aResult)
+nsDocument::GetElementById(const nsAString& aElementId)
 {
   nsCOMPtr<nsIAtom> idAtom(do_GetAtom(aElementId));
   if (!idAtom) {
-    *aResult = NS_ERROR_OUT_OF_MEMORY;
-
+    // This can only fail due to OOM when the atom doesn't exist, in which
+    // case there can't be an entry for it.
     return nsnull;
   }
 
   if (!CheckGetElementByIdArg(idAtom)) {
-    *aResult = NS_OK;
-
     return nsnull;
   }
 
-  nsIdentifierMapEntry *entry = GetElementByIdInternal(idAtom);
-  if (!entry) {
-    *aResult = NS_ERROR_OUT_OF_MEMORY;
-
-    return nsnull;
-  }
-
-  *aResult = NS_OK;
-
-  return entry->GetIdElement();
+  nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(idAtom);
+  return entry ? entry->GetIdElement() : nsnull;
 }
 
 NS_IMETHODIMP
 nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
 {
-  nsresult rv;
-  Element *content = GetElementById(aId, &rv);
+  Element *content = GetElementById(aId);
   if (content) {
-    rv = CallQueryInterface(content, aReturn);
-  }
-  else {
-    *aReturn = nsnull;
-  }
-
-  return rv;
+    return CallQueryInterface(content, aReturn);
+  }
+
+  *aReturn = nsnull;
+
+  return NS_OK;
 }
 
 Element*
 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
                                 void* aData)
 {
   if (!CheckGetElementByIdArg(aID))
     return nsnull;
 
-  nsIdentifierMapEntry *entry = GetElementByIdInternal(aID);
+  nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aID);
   NS_ENSURE_TRUE(entry, nsnull);
 
   entry->AddContentChangeCallback(aObserver, aData);
   return entry->GetIdElement();
 }
 
 void
 nsDocument::RemoveIDTargetObserver(nsIAtom* aID,
                                    IDTargetObserver aObserver, void* aData)
 {
   if (!CheckGetElementByIdArg(aID))
     return;
 
   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID);
   if (!entry) {
-    // We don't need to do the stuff that GetElementByIdInternal does;
-    // if there's no entry already in mIdentifierMap, then there's no
-    // callback to remove.
     return;
   }
 
   entry->RemoveContentChangeCallback(aObserver, aData);
 }
 
 void
 nsDocument::DispatchContentLoadedEvents()
@@ -7375,22 +7206,23 @@ nsDocument::ForgetLink(Link* aLink)
   nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
   NS_ASSERTION(entry || mStyledLinksCleared,
                "Document knows nothing about this Link!");
 #endif
   (void)mStyledLinks.RemoveEntry(aLink);
 }
 
 void
-nsDocument::DestroyLinkMap()
+nsDocument::DestroyElementMaps()
 {
 #ifdef DEBUG
   mStyledLinksCleared = true;
 #endif
   mStyledLinks.Clear();
+  mIdentifierMap.Clear();
 }
 
 static
 PLDHashOperator
 EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray)
 {
   nsTArray<Link*>* array = static_cast<nsTArray<Link*>*>(aArray);
   (void)array->AppendElement(aEntry->GetKey());
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -646,16 +646,27 @@ public:
   virtual nsIScriptGlobalObject* GetScopeObject();
 
   /**
    * Get the script loader for this document
    */
   virtual nsScriptLoader* ScriptLoader();
 
   /**
+   * Add/Remove an element to the document's id and name hashes
+   */
+  virtual void AddToIdTable(mozilla::dom::Element* aElement, nsIAtom* aId);
+  virtual void RemoveFromIdTable(mozilla::dom::Element* aElement,
+                                 nsIAtom* aId);
+  virtual void AddToNameTable(mozilla::dom::Element* aElement,
+                              nsIAtom* aName);
+  virtual void RemoveFromNameTable(mozilla::dom::Element* aElement,
+                                   nsIAtom* aName);
+
+  /**
    * Add a new observer of document change notifications. Whenever
    * content is changed, appended, inserted or removed the observers are
    * informed.
    */
   virtual void AddObserver(nsIDocumentObserver* aObserver);
 
   /**
    * Remove an observer of document change notifications. This will
@@ -802,23 +813,16 @@ public:
   NS_DECL_NSIDOMEVENTTARGET
 
   // nsIDOM3EventTarget
   NS_DECL_NSIDOM3EVENTTARGET
 
   // nsIDOMNSEventTarget
   NS_DECL_NSIDOMNSEVENTTARGET
 
-  // nsIMutationObserver
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
-  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
-  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
-
   // nsIScriptObjectPrincipal
   virtual nsIPrincipal* GetPrincipal();
 
   // nsIApplicationCacheContainer
   NS_DECL_NSIAPPLICATIONCACHECONTAINER
 
   virtual nsresult Init();
   
@@ -932,27 +936,20 @@ public:
   virtual void SetChangeScrollPosWhenScrollingToRef(PRBool aValue);
 
   already_AddRefed<nsContentList>
     GetElementsByTagName(const nsAString& aTagName);
   already_AddRefed<nsContentList>
     GetElementsByTagNameNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName);
 
-  virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId,
-                                                nsresult *aResult);
+  virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId);
 
 protected:
   friend class nsNodeUtils;
-  void RegisterNamedItems(nsIContent *aContent);
-  void UnregisterNamedItems(nsIContent *aContent);
-  void UpdateNameTableEntry(Element *aElement);
-  void UpdateIdTableEntry(Element *aElement);
-  void RemoveFromNameTable(Element *aElement);
-  void RemoveFromIdTable(Element *aElement);
 
   /**
    * Check that aId is not empty and log a message to the console
    * service if it is.
    * @returns PR_TRUE if aId looks correct, PR_FALSE otherwise.
    */
   static PRBool CheckGetElementByIdArg(const nsIAtom* aId);
   nsIdentifierMapEntry* GetElementByIdInternal(nsIAtom* aID);
@@ -961,18 +958,18 @@ protected:
 
   void RetrieveRelevantHeaders(nsIChannel *aChannel);
 
   static PRBool TryChannelCharset(nsIChannel *aChannel,
                                   PRInt32& aCharsetSource,
                                   nsACString& aCharset);
 
   // Call this before the document does something that will unbind all content.
-  // That will stop us from resolving URIs for all links as they are removed.
-  void DestroyLinkMap();
+  // That will stop us from doing a lot of work as each element is removed.
+  void DestroyElementMaps();
 
   // Refreshes the hrefs of all the links in the document.
   void RefreshLinkHrefs();
 
   nsIContent* GetFirstBaseNodeWithHref();
   nsresult SetFirstBaseNodeWithHref(nsIContent *node);
 
   // Get the first <title> element with the given IsNodeOfType type, or
--- a/content/base/src/nsDocumentFragment.cpp
+++ b/content/base/src/nsDocumentFragment.cpp
@@ -149,16 +149,19 @@ public:
   }
   virtual const nsAttrName* GetAttrNameAt(PRUint32 aIndex) const
   {
     return nsnull;
   }
 
   virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
 
+  virtual nsIAtom* DoGetID() const;
+  virtual nsIAtom *GetIDAttributeName() const;
+
 protected:
   nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 };
 
 nsresult
 NS_NewDocumentFragment(nsIDOMDocumentFragment** aInstancePtrResult,
                        nsNodeInfoManager *aNodeInfoManager)
 {
@@ -190,16 +193,28 @@ nsDocumentFragment::~nsDocumentFragment(
 }
 
 PRBool
 nsDocumentFragment::IsNodeOfType(PRUint32 aFlags) const
 {
   return !(aFlags & ~(eCONTENT | eDOCUMENT_FRAGMENT));
 }
 
+nsIAtom*
+nsDocumentFragment::DoGetID() const
+{
+  return nsnull;  
+}
+
+nsIAtom*
+nsDocumentFragment::GetIDAttributeName() const
+{
+  return nsnull;
+}
+
 DOMCI_DATA(DocumentFragment, nsDocumentFragment)
 
 // QueryInterface implementation for nsDocumentFragment
 NS_INTERFACE_TABLE_HEAD(nsDocumentFragment)
   NS_NODE_INTERFACE_TABLE2(nsDocumentFragment, nsIDOMNode,
                            nsIDOMDocumentFragment)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DocumentFragment)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericElement)
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -1103,17 +1103,17 @@ already_AddRefed<nsIAtom>
 nsGenericDOMDataNode::GetCurrentValueAtom()
 {
   nsAutoString val;
   GetData(val);
   return NS_NewAtom(val);
 }
 
 nsIAtom*
-nsGenericDOMDataNode::GetID() const
+nsGenericDOMDataNode::DoGetID() const
 {
   return nsnull;
 }
 
 const nsAttrValue*
 nsGenericDOMDataNode::DoGetClasses() const
 {
   NS_NOTREACHED("Shouldn't ever be called");
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -243,17 +243,17 @@ public:
 #endif
 
   virtual nsIContent *GetBindingParent() const;
   virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
 
   virtual already_AddRefed<nsIURI> GetBaseURI() const;
   virtual PRBool IsLink(nsIURI** aURI) const;
 
-  virtual nsIAtom* GetID() const;
+  virtual nsIAtom* DoGetID() const;
   virtual const nsAttrValue* DoGetClasses() const;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
   virtual nsICSSStyleRule* GetInlineStyleRule();
   NS_IMETHOD SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify);
   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                               PRInt32 aModType) const;
   virtual nsIAtom *GetClassAttributeName() const;
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -3244,48 +3244,16 @@ nsGenericElement::DispatchDOMEvent(nsEve
                                    nsPresContext* aPresContext,
                                    nsEventStatus* aEventStatus)
 {
   return nsEventDispatcher::DispatchDOMEvent(static_cast<nsIContent*>(this),
                                              aEvent, aDOMEvent,
                                              aPresContext, aEventStatus);
 }
 
-nsIAtom*
-nsGenericElement::GetID() const
-{
-  if (!HasFlag(NODE_MAY_HAVE_ID)) {
-    return nsnull;
-  }
-
-  nsIAtom* IDName = GetIDAttributeName();
-  if (IDName) {
-    const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(IDName);
-    if (attrVal){
-      if (attrVal->Type() == nsAttrValue::eAtom) {
-        return attrVal->GetAtomValue();
-      }
-      if(attrVal->IsEmptyString()){
-        return nsnull;
-      }
-      // Check if the ID has been stored as a string.
-      // This would occur if the ID attribute name changed after 
-      // the ID was parsed. 
-      if (attrVal->Type() == nsAttrValue::eString) {
-        nsAutoString idVal(attrVal->GetStringValue());
-
-        // Create an atom from the value and set it into the attribute list. 
-        const_cast<nsAttrValue*>(attrVal)->ParseAtom(idVal);
-        return attrVal->GetAtomValue();
-      }
-    }
-  }
-  return nsnull;
-}
-
 const nsAttrValue*
 nsGenericElement::DoGetClasses() const
 {
   NS_NOTREACHED("Shouldn't ever be called");
   return nsnull;
 }
 
 NS_IMETHODIMP
@@ -3367,22 +3335,16 @@ nsGenericElement::IsAttributeMapped(cons
 nsChangeHint
 nsGenericElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
                                          PRInt32 aModType) const
 {
   return nsChangeHint(0);
 }
 
 nsIAtom *
-nsGenericElement::GetIDAttributeName() const
-{
-  return mNodeInfo->GetIDAttributeAtom();
-}
-
-nsIAtom *
 nsGenericElement::GetClassAttributeName() const
 {
   return nsnull;
 }
 
 PRBool
 nsGenericElement::FindAttributeDependence(const nsIAtom* aAttribute,
                                           const MappedAttributeEntry* const aMaps[],
@@ -4727,25 +4689,16 @@ nsGenericElement::SetAttrAndNotify(PRInt
 }
 
 PRBool
 nsGenericElement::ParseAttribute(PRInt32 aNamespaceID,
                                  nsIAtom* aAttribute,
                                  const nsAString& aValue,
                                  nsAttrValue& aResult)
 {
-  if (aNamespaceID == kNameSpaceID_None &&
-      aAttribute == GetIDAttributeName() && !aValue.IsEmpty()) {
-    SetFlags(NODE_MAY_HAVE_ID);
-    // Store id as an atom.  id="" means that the element has no id,
-    // not that it has an emptystring as the id.
-    aResult.ParseAtom(aValue);
-    return PR_TRUE;
-  }
-
   return PR_FALSE;
 }
 
 PRBool
 nsGenericElement::SetMappedAttribute(nsIDocument* aDocument,
                                      nsIAtom* aName,
                                      nsAttrValue& aValue,
                                      nsresult* aRetval)
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -374,17 +374,16 @@ public:
 
   // nsIContent interface methods
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
                               PRBool aNullParent = PR_TRUE);
   virtual already_AddRefed<nsINodeList> GetChildren(PRInt32 aChildType);
-  virtual nsIAtom *GetIDAttributeName() const;
   virtual nsIAtom *GetClassAttributeName() const;
   virtual already_AddRefed<nsINodeInfo> GetExistingAttrNameFromQName(const nsAString& aStr) const;
   nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                    const nsAString& aValue, PRBool aNotify)
   {
     return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
   }
   virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
@@ -446,17 +445,16 @@ public:
   {
     List(out, aIndent, EmptyCString());
   }
   virtual void DumpContent(FILE* out, PRInt32 aIndent, PRBool aDumpAll) const;
   void List(FILE* out, PRInt32 aIndent, const nsCString& aPrefix) const;
   void ListAttributes(FILE* out) const;
 #endif
 
-  virtual nsIAtom* GetID() const;
   virtual const nsAttrValue* DoGetClasses() const;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
   virtual nsICSSStyleRule* GetInlineStyleRule();
   NS_IMETHOD SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify);
   NS_IMETHOD_(PRBool)
     IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                               PRInt32 aModType) const;
@@ -989,25 +987,39 @@ protected:
   void UnregisterFreezableElement() {
     nsIDocument* doc = GetOwnerDoc();
     if (doc) {
       doc->UnregisterFreezableElement(this);
     }
   }
 
   /**
-   * GetContentsAsText will take all the textnodes that are children
-   * of |this| and concatenate the text in them into aText.  It
-   * completely ignores any non-text-node children of |this|; in
-   * particular it does not descend into any children of |this| that
-   * happen to be container elements.
-   *
-   * @param aText the resulting text [OUT]
+   * Add/remove this element to the documents id cache
    */
-  void GetContentsAsText(nsAString& aText);
+  void AddToIdTable(nsIAtom* aId) {
+    NS_ASSERTION(HasFlag(NODE_HAS_ID), "Node lacking NODE_HAS_ID flag");
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
+      doc->AddToIdTable(this, aId);
+    }
+  }
+  void RemoveFromIdTable() {
+    if (HasFlag(NODE_HAS_ID)) {
+      nsIDocument* doc = GetCurrentDoc();
+      if (doc) {
+        nsIAtom* id = DoGetID();
+        // id can be null during mutation events evilness. Also, XUL elements
+        // loose their proto attributes during cc-unlink, so this can happen
+        // during cc-unlink too.
+        if (id) {
+          doc->RemoveFromIdTable(this, DoGetID());
+        }
+      }
+    }
+  }
 
   /**
    * Functions to carry out event default actions for links of all types
    * (HTML links, XLinks, SVG "XLinks", etc.)
    */
 
   /**
    * Check that we meet the conditions to handle a link event
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -573,23 +573,16 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
     // aNode's children.
     PRUint32 i, length = aNode->GetChildCount();
     for (i = 0; i < length; ++i) {
       nsCOMPtr<nsINode> child;
       rv = CloneAndAdopt(aNode->GetChildAt(i), aClone, PR_TRUE, nodeInfoManager,
                          aCx, aOldScope, aNewScope, aNodesWithProperties,
                          clone, getter_AddRefs(child));
       NS_ENSURE_SUCCESS(rv, rv);
-      if (isDeepDocumentClone) {
-        NS_ASSERTION(child->IsNodeOfType(nsINode::eCONTENT),
-                     "A clone of a child of a node is not nsIContent?");
-
-        nsIContent* content = static_cast<nsIContent*>(child.get());
-        static_cast<nsDocument*>(clone.get())->RegisterNamedItems(content);
-      }
     }
   }
 
   // XXX setting document on some nodes not in a document so XBL will bind
   // and chrome won't break. Make XBL bind to document-less nodes!
   // XXXbz Once this is fixed, fix up the asserts in all implementations of
   // BindToTree to assert what they would like to assert, and fix the
   // ChangeDocumentFor() call in nsXULElement::BindToTree as well.  Also,
--- a/content/base/src/nsStyledElement.cpp
+++ b/content/base/src/nsStyledElement.cpp
@@ -66,16 +66,30 @@ nsStyledElement::GetClassAttributeName()
 }
 
 nsIAtom*
 nsStyledElement::GetIDAttributeName() const
 {
   return nsGkAtoms::id;
 }
 
+nsIAtom*
+nsStyledElement::DoGetID() const
+{
+  NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
+
+  // The nullcheck here is needed because nsGenericElement::UnsetAttr calls
+  // out to various code between removing the attribute and we get a chance to
+  // clear the NODE_HAS_ID flag.
+
+  const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::id);
+
+  return attr ? attr->GetAtomValue() : nsnull;
+}
+
 const nsAttrValue*
 nsStyledElement::DoGetClasses() const
 {
   NS_ASSERTION(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call");
   return mAttrsAndChildren.GetAttr(nsGkAtoms::_class);
 }
 
 PRBool
@@ -88,22 +102,55 @@ nsStyledElement::ParseAttribute(PRInt32 
       ParseStyleAttribute(aValue, aResult, PR_FALSE);
       return PR_TRUE;
     }
     if (aAttribute == nsGkAtoms::_class) {
       SetFlags(NODE_MAY_HAVE_CLASS);
       aResult.ParseAtomArray(aValue);
       return PR_TRUE;
     }
+    if (aAttribute == nsGkAtoms::id) {
+      // Store id as an atom.  id="" means that the element has no id,
+      // not that it has an emptystring as the id.
+      RemoveFromIdTable();
+      if (aValue.IsEmpty()) {
+        UnsetFlags(NODE_HAS_ID);
+        return PR_FALSE;
+      }
+      aResult.ParseAtom(aValue);
+      SetFlags(NODE_HAS_ID);
+      AddToIdTable(aResult.GetAtomValue());
+      return PR_TRUE;
+    }
   }
 
   return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                              aResult);
 }
 
+nsresult
+nsStyledElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
+                           PRBool aNotify)
+{
+  PRBool isId = PR_FALSE;
+  if (aAttribute == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) {
+    // Have to do this before clearing flag. See RemoveFromIdTable
+    RemoveFromIdTable();
+    isId = PR_TRUE;
+  }
+  
+  nsresult rv = nsGenericElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
+
+  if (isId) {
+    UnsetFlags(NODE_HAS_ID);
+  }
+
+  return rv;
+}
+
 NS_IMETHODIMP
 nsStyledElement::SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify)
 {
   SetFlags(NODE_MAY_HAVE_STYLE);
   PRBool modification = PR_FALSE;
   nsAutoString oldValueStr;
 
   PRBool hasListeners = aNotify &&
@@ -158,23 +205,38 @@ nsStyledElement::BindToTree(nsIDocument*
                             nsIContent* aBindingParent,
                             PRBool aCompileEventHandlers)
 {
   nsresult rv = nsStyledElementBase::BindToTree(aDocument, aParent,
                                                 aBindingParent,
                                                 aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // XXXbz if we already have a style attr parsed, this won't do
-  // anything... need to fix that.
-  ReparseStyleAttribute(PR_FALSE);
+  if (aDocument && HasFlag(NODE_HAS_ID) && !GetBindingParent()) {
+    aDocument->AddToIdTable(this, DoGetID());
+  }
+
+  if (!IsXUL()) {
+    // XXXbz if we already have a style attr parsed, this won't do
+    // anything... need to fix that.
+    ReparseStyleAttribute(PR_FALSE);
+  }
 
-  return rv;
+  return NS_OK;
 }
 
+void
+nsStyledElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
+{
+  RemoveFromIdTable();
+
+  nsStyledElementBase::UnbindFromTree(aDeep, aNullParent);
+}
+
+
 // ---------------------------------------------------------------
 // Others and helpers
 
 nsresult
 nsStyledElement::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
 {
   nsGenericElement::nsDOMSlots *slots = GetDOMSlots();
   NS_ENSURE_TRUE(slots, NS_ERROR_OUT_OF_MEMORY);
--- a/content/base/src/nsStyledElement.h
+++ b/content/base/src/nsStyledElement.h
@@ -59,27 +59,32 @@ class nsStyledElement : public nsStyledE
 protected:
 
   inline nsStyledElement(nsINodeInfo *aNodeInfo)
     : nsStyledElementBase(aNodeInfo)
   {}
 
 public:
 
-  // nsIContent interface methods for styling
+  // nsIContent interface methods
   virtual nsIAtom* GetClassAttributeName() const;
   virtual nsIAtom* GetIDAttributeName() const;
+  virtual nsIAtom* DoGetID() const;
   virtual const nsAttrValue* DoGetClasses() const;
 
   virtual nsICSSStyleRule* GetInlineStyleRule();
   NS_IMETHOD SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify);
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
+  virtual void UnbindFromTree(PRBool aDeep, PRBool aNullParent);
+
+  virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
+                             PRBool aNotify);
 
 protected:
 
   /**
    * Parse a style attr value into a CSS rulestruct (or, if there is no
    * document, leave it as a string) and return as nsAttrValue.
    *
    * @param aValue the value to parse
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -338,16 +338,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug543870.html \
 		file_bug543870_img.jpg \
 		file_bug543870_inner.html \
 		file_bug543870_doc.html \
 		file_bug543870_text.txt \
 		test_bug475156.html \
 		bug475156.sjs \
 		test_bug544642.html \
+		test_bug564863.xhtml \
 		test_copypaste.html \
 		test_bug503481.html \
 		file_bug503481.sjs \
 		test_bug503481b.html \
 		file_bug503481b_inner.html \
 		test_viewport_scroll.html \
 		test_CSP.html \
 		file_CSP.sjs \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug564863.xhtml
@@ -0,0 +1,328 @@
+<!DOCTYPE html [
+<!ATTLIST ns:x id ID #REQUIRED>
+<!ATTLIST ns2:x id_2 ID #REQUIRED>
+]>
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+      xmlns:svg="http://www.w3.org/2000/svg"
+      xmlns:ns="urn:namespace"
+      xmlns:ns2="urn:namespace">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=564863
+-->
+<head>
+  <title>Test for Bug 564863</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<style>
+#div_id {
+  color: rgb(10, 10, 10);
+}
+#a_id {
+  color: rgb(20, 20, 20);
+}
+#xul_id {
+  color: rgb(30, 30, 30);
+}
+#svg_id {
+  color: rgb(40, 40, 40);
+}
+#ns_id {
+  color: rgb(50, 50, 50);
+}
+#ns2_id {
+  color: rgb(60, 60, 60);
+}
+</style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=564863">Mozilla Bug 564863</a>
+<!-- Elements to ensure we have nodeinfos with id-attribute set -->
+<div><ns:x id="ns-holder"/><ns2:x id_2="ns2-holder"/></div>
+
+<!-- DOM to muck around with for tests -->
+<p id="root">
+<div id="div_id" />
+<a id="a_id" />
+<xul:button id="xul_id" />
+<svg:svg><svg:g id="svg_id" /></svg:svg>
+<ns:x id="ns_id" />
+</p>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+root = $('root');
+div = root.children[0];
+a = root.children[1];
+xul = root.children[2];
+svg = root.children[3].firstChild;
+nsx = root.children[4];
+
+var div_cs = getComputedStyle(div, "");
+var a_cs = getComputedStyle(a, "");
+var xul_cs = getComputedStyle(xul, "");
+var svg_cs = getComputedStyle(svg, "");
+var nsx_cs = getComputedStyle(nsx, "");
+
+function checkHasId(test) {
+  // Check computed style first to avoid flushes from hiding problems
+  checkHasIdNoGEBI(test);
+
+  is($("div_id"), div, "div getElementById " + test);
+  is($("a_id"),   a,   "a getElementById " + test);
+  is($("xul_id"), xul, "xul getElementById " + test);
+  is($("svg_id"), svg, "svg getElementById " + test);
+  is($("ns_id"),  nsx, "ns getElementById " + test);
+}
+
+function checkHasIdNoGEBI(test) {
+  is(div_cs.color, "rgb(10, 10, 10)", "div color " + test);
+  is(a_cs.color,   "rgb(20, 20, 20)", "a color " + test);
+  is(xul_cs.color, "rgb(30, 30, 30)", "xul color " + test);
+  is(svg_cs.color, "rgb(40, 40, 40)", "svg color " + test);
+  is(nsx_cs.color, "rgb(50, 50, 50)", "nsx color " + test);
+
+  is(div.id, "div_id", "div id " + test);
+  is(a.id,   "a_id",   "a id " + test);
+  is(xul.id, "xul_id", "xul id " + test);
+  is(svg.id, "svg_id", "svg id " + test);
+  is (nsx.getAttribute("id"), "ns_id", "ns id " + test);
+}
+
+function checkHasNoId(removed, test) {
+  is(div_cs.color, "rgb(0, 0, 0)", "div color " + test);
+  is(a_cs.color,   "rgb(0, 0, 0)", "a color " + test);
+  is(xul_cs.color, "rgb(0, 0, 0)", "xul color " + test);
+  is(svg_cs.color, "rgb(0, 0, 0)", "svg color " + test);
+  is(nsx_cs.color, "rgb(0, 0, 0)", "nsx color " + test);
+
+  attrValue = removed ? null : "";
+
+  is(div.id, "", "div id " + test);
+  is(a.id,   "", "a id " + test);
+  is(xul.id, "", "xul id " + test);
+  is(svg.id, "", "svg id " + test);
+
+  is(div.getAttribute("id"), attrValue, "div getAttribute " + test);
+  is(a.getAttribute("id"),   attrValue, "a getAttribute " + test);
+  is(xul.getAttribute("id"), "",        "xul getAttribute " + test);
+  is(svg.getAttribute("id"), attrValue, "svg getAttribute " + test);
+  is(nsx.getAttribute("id"), attrValue, "ns getAttribute " + test);
+  
+  is($("div_id"), null, "div getElementById " + test);
+  is($("a_id"),   null, "a getElementById " + test);
+  is($("xul_id"), null, "xul getElementById " + test);
+  is($("svg_id"), null, "svg getElementById " + test);
+  is($("ns_id"),  null, "ns getElementById " + test);
+}
+
+// Check that dynamic modifications of attribute work
+
+checkHasId("in markup");
+
+div.id = "";
+a.id = "";
+xul.id = "";
+svg.id = "";
+nsx.setAttribute("id", "");
+
+checkHasNoId(false, "set to empty");
+
+div.id = "div_id";
+a.id = "a_id";
+xul.id = "xul_id";
+svg.id = "svg_id";
+nsx.setAttribute("id", "ns_id");
+
+checkHasId("set using .id");
+
+div.setAttribute("id", "");
+a.setAttribute("id",   "");
+xul.setAttribute("id", "");
+svg.setAttribute("id", "");
+nsx.setAttribute("id", "");
+
+checkHasNoId(false, "setAttribute to empty");
+
+div.id = "div_id";
+a.id = "a_id";
+xul.id = "xul_id";
+svg.id = "svg_id";
+nsx.setAttribute("id", "ns_id");
+
+checkHasId("set again using .id");
+
+div.removeAttribute("id");
+a.removeAttribute("id");
+xul.removeAttribute("id");
+svg.removeAttribute("id");
+nsx.removeAttribute("id");
+
+checkHasNoId(true, "removed attribute");
+
+div.setAttribute("id", "div_id");
+a.setAttribute("id",   "a_id");
+xul.setAttribute("id", "xul_id");
+svg.setAttribute("id", "svg_id");
+nsx.setAttribute("id", "ns_id");
+
+checkHasId("set using setAttribute");
+
+t1 = document.createElement("div");
+t1.id = "div_id";
+t2 = document.createElement("a");
+t2.id = "a_id";
+t3 = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "button");
+t3.id = "xul_id";
+t4 = document.createElementNS("http://www.w3.org/2000/svg", "g");
+t4.id = "svg_id";
+t5 = document.createElementNS("urn:namespace", "ns:x");
+t5.setAttribute("id", "ns_id");
+
+// Check that inserting elements before/after existing work
+
+function insertAfter(newChild, existing) {
+  existing.parentNode.insertBefore(newChild, existing.nextSibling);
+}
+function insertBefore(newChild, existing) {
+  existing.parentNode.insertBefore(newChild, existing);
+}
+function removeNode(child) {
+  child.parentNode.removeChild(child);
+}
+
+insertAfter(t1, div);
+insertAfter(t2, a);
+insertAfter(t3, xul);
+insertAfter(t4, svg);
+insertAfter(t5, nsx);
+
+checkHasId("inserted after");
+
+insertBefore(t1, div);
+insertBefore(t2, a);
+insertBefore(t3, xul);
+insertBefore(t4, svg);
+insertBefore(t5, nsx);
+
+checkHasIdNoGEBI("inserted before");
+is($("div_id"), t1, "div getElementById inserted before");
+is($("a_id"),   t2,   "a getElementById inserted before");
+is($("xul_id"), t3, "xul getElementById inserted before");
+is($("svg_id"), t4, "svg getElementById inserted before");
+is($("ns_id"),  t5, "ns getElementById inserted before");
+
+t1.removeAttribute("id");
+t2.removeAttribute("id");
+t3.removeAttribute("id");
+t4.removeAttribute("id");
+t5.removeAttribute("id");
+
+checkHasId("removed tx attribute");
+
+t1.setAttribute("id", "div_id");
+t2.setAttribute("id",   "a_id");
+t3.setAttribute("id", "xul_id");
+t4.setAttribute("id", "svg_id");
+t5.setAttribute("id", "ns_id");
+
+checkHasIdNoGEBI("setAttribute before");
+is($("div_id"), t1, "div getElementById setAttribute before");
+is($("a_id"),   t2,   "a getElementById setAttribute before");
+is($("xul_id"), t3, "xul getElementById setAttribute before");
+is($("svg_id"), t4, "svg getElementById setAttribute before");
+is($("ns_id"),  t5, "ns getElementById setAttribute before");
+
+removeNode(t1);
+removeNode(t2);
+removeNode(t3);
+removeNode(t4);
+removeNode(t5);
+
+checkHasId("removed temporaries");
+
+removeNode(div);
+removeNode(a);
+removeNode(xul);
+removeNode(svg);
+removeNode(nsx);
+
+checkHasIdNoGEBI("removed node");
+
+// Check that removing an element during UnsetAttr works
+is(div.id, "div_id", "div still has id set");
+var mutateFired = false;
+root.appendChild(div);
+var f = function(e) {
+  div.removeEventListener("DOMAttrModified", f, false);
+  is(e.target, div, "target is div");
+  is(div.id, "", "div no longer has id");
+  is(div.getAttribute("id"), null, "div no longer has id attr");
+  removeNode(div);
+  is(div.parentNode, null, "div was removed");
+  mutateFired = true;
+}
+div.addEventListener("DOMAttrModified", f, false);
+div.removeAttribute("id");
+ok(mutateFired, "mutation event fired");
+
+// Check same for XML elements
+is(nsx.getAttribute("id"), "ns_id", "nsx still has id set");
+mutateFired = false;
+root.appendChild(nsx);
+var f2 = function(e) {
+  nsx.removeEventListener("DOMAttrModified", f2, false);
+  is(e.target, nsx, "target is nsx");
+  is(nsx.getAttribute("id"), null, "nsx no longer has id attr");
+  removeNode(nsx);
+  is(nsx.parentNode, null, "nsx was removed");
+  mutateFired = true;
+}
+nsx.addEventListener("DOMAttrModified", f2, false);
+nsx.removeAttribute("id");
+ok(mutateFired, "mutation event fired");
+
+
+// Check that modifying the id-name of an XML element works
+ok($("ns-holder"), "ns-holder exists");
+ok($("ns2-holder"), "ns2-holder exists");
+root.appendChild(nsx);
+nsx.setAttribute("id", "ns_id");
+is(nsx_cs.color, "rgb(50, 50, 50)", "nsx has initial id");
+is($("ns_id"), nsx, "gEBI works on old id");
+nsx.setAttribute("id_2", "ns2_id");
+nsx.prefix = "ns2";
+removeNode(nsx);
+root.appendChild(nsx);
+is(nsx_cs.color, "rgb(60, 60, 60)", "nsx has new id");
+is($("ns2_id"), nsx, "gEBI works on new id");
+
+// Need to change id and then CC to ensure that we don't end up with dangling pointers
+function gc() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+        .getInterface(Components.interfaces.nsIDOMWindowUtils)
+        .garbageCollect();
+}
+ok($("ns-holder"), "ns-holder exists");
+ok($("ns2-holder"), "ns2-holder exists");
+tempNode = document.createElementNS("urn:namespace", "ns:x");
+tempNode.setAttribute("id", "tempNode_id");
+document.body.appendChild(tempNode);
+is($("tempNode_id"), tempNode, "tempNode in gEBI table");
+tempNode.prefix = "ns2";
+removeNode(tempNode);
+tempNode = null;
+gc();
+$("tempNode_id");
+ok(true, "Didn't crash when accessing old id");
+
+
+]]>
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -908,30 +908,34 @@ nsGenericHTMLElement::UpdateEditableStat
   // XXX Should we do this only when in a document?
   ContentEditableTristate value = GetContentEditableValue();
   if (value != eInherit) {
     SetEditableFlag(!!value);
 
     return;
   }
 
-  nsGenericElement::UpdateEditableState();
+  nsStyledElement::UpdateEditableState();
 }
 
 nsresult
 nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                  nsIContent* aBindingParent,
                                  PRBool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElementBase::BindToTree(aDocument, aParent,
                                                      aBindingParent,
                                                      aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument) {
+    if (HasFlag(NODE_HAS_NAME)) {
+      aDocument->
+        AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
+    }
     if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
       nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
       if (htmlDocument) {
         htmlDocument->ChangeContentEditableCount(this, +1);
       }
     }
   }
 
@@ -943,17 +947,17 @@ nsGenericHTMLElement::UnbindFromTree(PRB
 {
   if (GetContentEditableValue() == eTrue) {
     nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetCurrentDoc());
     if (htmlDocument) {
       htmlDocument->ChangeContentEditableCount(this, -1);
     }
   }
 
-  nsGenericElement::UnbindFromTree(aDeep, aNullParent);
+  nsStyledElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 nsHTMLFormElement*
 nsGenericHTMLElement::FindForm(nsHTMLFormElement* aCurrentForm)
 {
   // Make sure we don't end up finding a form that's anonymous from
   // our point of view.
   nsIContent* bindingParent = GetBindingParent();
@@ -1156,18 +1160,18 @@ nsGenericHTMLElement::SetAttr(PRInt32 aN
   PRBool contentEditable = aNameSpaceID == kNameSpaceID_None &&
                            aName == nsGkAtoms::contenteditable;
   PRInt32 change;
   if (contentEditable) {
     change = GetContentEditableValue() == eTrue ? -1 : 0;
     SetFlags(NODE_MAY_HAVE_CONTENT_EDITABLE_ATTR);
   }
 
-  nsresult rv = nsGenericElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
-                                          aNotify);
+  nsresult rv = nsStyledElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
+                                         aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (contentEditable) {
     if (aValue.IsEmpty() || aValue.LowerCaseEqualsLiteral("true")) {
       change += 1;
     }
 
     ChangeEditableState(change);
@@ -1180,17 +1184,22 @@ nsresult
 nsGenericHTMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                                 PRBool aNotify)
 {
   PRBool contentEditable = PR_FALSE;
   PRInt32 contentEditableChange;
 
   // Check for event handlers
   if (aNameSpaceID == kNameSpaceID_None) {
-    if (aAttribute == nsGkAtoms::contenteditable) {
+    if (aAttribute == nsGkAtoms::name) {
+      // Have to do this before clearing flag. See RemoveFromNameTable
+      RemoveFromNameTable();
+      UnsetFlags(NODE_HAS_NAME);
+    }
+    else if (aAttribute == nsGkAtoms::contenteditable) {
       contentEditable = PR_TRUE;
       contentEditableChange = GetContentEditableValue() == eTrue ? -1 : 0;
     }
     else if (nsContentUtils::IsEventAttributeName(aAttribute,
                                                   EventNameType_HTML)) {
       nsIEventListenerManager* manager = GetListenerManager(PR_FALSE);
       if (manager) {
         manager->RemoveScriptEventListener(aAttribute);
@@ -1217,36 +1226,57 @@ nsGenericHTMLElement::GetBaseTarget(nsAS
     ownerDoc->GetBaseTarget(aBaseTarget);
   } else {
     aBaseTarget.Truncate();
   }
 }
 
 //----------------------------------------------------------------------
 
+static PRBool
+CanHaveName(nsIAtom* aTag)
+{
+  return aTag == nsGkAtoms::img ||
+         aTag == nsGkAtoms::form ||
+         aTag == nsGkAtoms::applet ||
+         aTag == nsGkAtoms::embed ||
+         aTag == nsGkAtoms::object;
+}
 
 PRBool
 nsGenericHTMLElement::ParseAttribute(PRInt32 aNamespaceID,
                                      nsIAtom* aAttribute,
                                      const nsAString& aValue,
                                      nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::dir) {
       return aResult.ParseEnumValue(aValue, kDirTable, PR_FALSE);
     }
   
     if (aAttribute == nsGkAtoms::tabindex) {
       return aResult.ParseIntWithBounds(aValue, -32768, 32767);
     }
 
-    if (aAttribute == nsGkAtoms::name && !aValue.IsEmpty()) {
+    if (aAttribute == nsGkAtoms::name) {
       // Store name as an atom.  name="" means that the element has no name,
       // not that it has an emptystring as the name.
+      RemoveFromNameTable();
+      if (aValue.IsEmpty()) {
+        UnsetFlags(NODE_HAS_NAME);
+        return PR_FALSE;
+      }
+
       aResult.ParseAtom(aValue);
+
+      if (CanHaveName(Tag())) {
+        SetFlags(NODE_HAS_NAME);
+        AddToNameTable(aResult.GetAtomValue());
+      }
+      
       return PR_TRUE;
     }
 
     if (aAttribute == nsGkAtoms::contenteditable) {
       aResult.ParseAtom(aValue);
       return PR_TRUE;
     }
   }
@@ -2496,16 +2526,18 @@ nsGenericHTMLFormElement::BindToTree(nsI
   return NS_OK;
 }
 
 void
 nsGenericHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
 {
   // Save state before doing anything
   SaveState();
+  
+  RemoveFromNameTable();
 
   if (mForm) {
     // Might need to unset mForm
     if (aNullParent) {
       // No more parent means no more form
       ClearForm(PR_TRUE, PR_TRUE);
     } else {
       // Recheck whether we should still have an mForm.
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -494,16 +494,36 @@ public:
   /**
    * Locate an nsIEditor rooted at this content node, if there is one.
    */
   NS_HIDDEN_(nsresult) GetEditor(nsIEditor** aEditor);
   NS_HIDDEN_(nsresult) GetEditorInternal(nsIEditor** aEditor);
 
 protected:
   /**
+   * Add/remove this element to the documents name cache
+   */
+  void AddToNameTable(nsIAtom* aName) {
+    NS_ASSERTION(HasFlag(NODE_HAS_NAME), "Node lacking NODE_HAS_NAME flag");
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc && !IsInAnonymousSubtree()) {
+      doc->AddToNameTable(this, aName);
+    }
+  }
+  void RemoveFromNameTable() {
+    if (HasFlag(NODE_HAS_NAME)) {
+      nsIDocument* doc = GetCurrentDoc();
+      if (doc) {
+        doc->RemoveFromNameTable(this, GetParsedAttr(nsGkAtoms::name)->
+                                         GetAtomValue());
+      }
+    }
+  }
+
+  /**
    * Register or unregister an access key to this element based on the
    * accesskey attribute.
    */
   void RegAccessKey()
   {
     if (HasFlag(NODE_HAS_ACCESSKEY)) {
       RegUnRegAccessKey(PR_TRUE);
     }
--- a/content/html/content/src/nsHTMLLabelElement.cpp
+++ b/content/html/content/src/nsHTMLLabelElement.cpp
@@ -431,19 +431,17 @@ nsHTMLLabelElement::GetControlContent()
 
   // We have a @for. The id has to be linked to an element in the same document
   // and this element should be a labelable form control.
   nsIDocument* doc = GetCurrentDoc();
   if (!doc) {
     return nsnull;
   }
 
-  nsresult rv;
-  nsIContent* content = doc->GetElementById(elementId, &rv);
-  NS_ENSURE_SUCCESS(rv, nsnull);
+  nsIContent* content = doc->GetElementById(elementId);
   if (!content) {
     return nsnull;
   }
 
   nsCOMPtr<nsIFormControl> element = do_QueryInterface(content);
   if (element && element->IsLabelableControl()) {
     NS_ADDREF(content);
     return content;
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2630,36 +2630,28 @@ static void
 FindNamedItems(nsIAtom* aName, nsIContent *aContent,
                nsIdentifierMapEntry* aEntry)
 {
   NS_ASSERTION(aEntry->HasNameContentList(),
                "Entry w/o content list passed to FindNamedItems()!");
   NS_ASSERTION(!aEntry->IsInvalidName(),
                "Entry that should never have a list passed to FindNamedItems()!");
 
-  if (!aContent->IsElement()) {
-    // non-elements are not named items nor can they have children.
-    return;
-  }
-
-  Element* element = aContent->AsElement();
-
-  if (aName == nsContentUtils::IsNamedItem(element)) {
-    aEntry->AddNameElement(element);
+  if (aContent->HasFlag(NODE_HAS_NAME)) {
+    NS_ASSERTION(nsGenericHTMLElement::FromContent(aContent),
+                 "Only HTML Elements should have a name");
+  
+    nsGenericHTMLElement* elm = static_cast<nsGenericHTMLElement*>(aContent);
+    if (elm->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == aName) {
+      aEntry->AddNameElement(elm);
+    }
   }
 
-  if (!aEntry->GetIdElement() &&
-      // Maybe this node has the right id?
-      aName == element->GetID()) {
-    aEntry->AddIdElement(element);
-  }
-
-  PRUint32 i, count = element->GetChildCount();
-  for (i = 0; i < count; ++i) {
-    FindNamedItems(aName, element->GetChildAt(i), aEntry);
+  for (nsINode::ChildIterator iter(aContent); !iter.IsDone(); iter.Next()) {
+    FindNamedItems(aName, iter, aEntry);
   }
 }
 
 nsresult
 nsHTMLDocument::ResolveName(const nsAString& aName,
                             nsIDOMHTMLFormElement *aForm,
                             nsISupports **aResult)
 {
@@ -2672,37 +2664,17 @@ nsHTMLDocument::ResolveName(const nsAStr
   nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(name);
   NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
 
   if (entry->IsInvalidName()) {
     // There won't be any named items by this name -- it's reserved
     return NS_OK;
   }
 
-  // Now we know we _might_ have items.  Before looking at
-  // entry->mNameContentList, make sure to flush out content (see
-  // bug 69826).
-  // This is a perf killer while the document is loading!
-
-  // Make sure to stash away the current generation so we can check whether the
-  // table changes when we flush.
-  PRUint32 generation = mIdentifierMap.GetGeneration();
-  
-  // If we already have an entry->mNameContentList, we need to flush out
-  // notifications too, so that it will get updated properly.
-  FlushPendingNotifications(entry->HasNameContentList() ?
-                            Flush_ContentAndNotify : Flush_Content);
-
-  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(name);
-    NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
-  }
+  // Now we know we _might_ have items.
 
   if (!entry->HasNameContentList()) {
 #ifdef DEBUG_jst
     {
       printf ("nsHTMLDocument name cache miss for name '%s'\n",
               NS_ConvertUTF16toUTF8(aName).get());
     }
 #endif
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -235,20 +235,19 @@ public:
   }
 
   virtual nsresult SetEditingState(EditingState aState);
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual NS_HIDDEN_(void) RemovedFromDocShell();
 
-  virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId,
-                                                nsresult *aResult)
+  virtual mozilla::dom::Element *GetElementById(const nsAString& aElementId)
   {
-    return nsDocument::GetElementById(aElementId, aResult);
+    return nsDocument::GetElementById(aElementId);
   }
 
 protected:
   nsresult GetBodySize(PRInt32* aWidth,
                        PRInt32* aHeight);
 
   nsresult PrePopulateIdentifierMap();
 
--- a/content/xml/content/src/nsXMLElement.cpp
+++ b/content/xml/content/src/nsXMLElement.cpp
@@ -58,13 +58,118 @@ NS_INTERFACE_TABLE_HEAD(nsXMLElement)
   NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsXMLElement)
     NS_INTERFACE_TABLE_ENTRY(nsXMLElement, nsIDOMNode)
     NS_INTERFACE_TABLE_ENTRY(nsXMLElement, nsIDOMElement)
   NS_OFFSET_AND_INTERFACE_TABLE_END
   NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Element)
 NS_ELEMENT_INTERFACE_MAP_END
 
-
 NS_IMPL_ADDREF_INHERITED(nsXMLElement, nsGenericElement)
 NS_IMPL_RELEASE_INHERITED(nsXMLElement, nsGenericElement)
 
 NS_IMPL_ELEMENT_CLONE(nsXMLElement)
+
+nsresult
+nsXMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
+                        PRBool aNotify)
+{
+  PRBool isId = PR_FALSE;
+  if (aAttribute == GetIDAttributeName() &&
+      aNameSpaceID == kNameSpaceID_None) {
+    // Have to do this before clearing flag. See RemoveFromIdTable
+    RemoveFromIdTable();
+    isId = PR_TRUE;
+  }
+  
+  nsresult rv = nsGenericElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
+  
+  if (isId) {
+    UnsetFlags(NODE_HAS_ID);
+  }
+  
+  return rv;
+}
+
+nsIAtom *
+nsXMLElement::GetIDAttributeName() const
+{
+  return mNodeInfo->GetIDAttributeAtom();
+}
+
+nsIAtom*
+nsXMLElement::DoGetID() const
+{
+  NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
+
+  nsIAtom* IDName = GetIDAttributeName();
+  if (IDName) {
+    const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(IDName);
+    if (attrVal) {
+      if (attrVal->Type() == nsAttrValue::eAtom) {
+        return attrVal->GetAtomValue();
+      }
+      if (attrVal->IsEmptyString()) {
+        return nsnull;
+      }
+      // Check if the ID has been stored as a string.
+      // This would occur if the ID attribute name changed after 
+      // the ID was parsed. 
+      if (attrVal->Type() == nsAttrValue::eString) {
+        nsAutoString idVal(attrVal->GetStringValue());
+
+        // Create an atom from the value and set it into the attribute list. 
+        const_cast<nsAttrValue*>(attrVal)->ParseAtom(idVal);
+        return attrVal->GetAtomValue();
+      }
+    }
+  }
+  return nsnull;
+}
+
+PRBool
+nsXMLElement::ParseAttribute(PRInt32 aNamespaceID,
+                             nsIAtom* aAttribute,
+                             const nsAString& aValue,
+                             nsAttrValue& aResult)
+{
+  if (aAttribute == GetIDAttributeName() &&
+      aNamespaceID == kNameSpaceID_None) {
+    // Store id as an atom.  id="" means that the element has no id,
+    // not that it has an emptystring as the id.
+    RemoveFromIdTable();
+    if (aValue.IsEmpty()) {
+      UnsetFlags(NODE_HAS_ID);
+      return PR_FALSE;
+    }
+    aResult.ParseAtom(aValue);
+    SetFlags(NODE_HAS_ID);
+    AddToIdTable(aResult.GetAtomValue());
+    return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+
+nsresult
+nsXMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                         nsIContent* aBindingParent,
+                         PRBool aCompileEventHandlers)
+{
+  nsresult rv = nsGenericElement::BindToTree(aDocument, aParent,
+                                             aBindingParent,
+                                             aCompileEventHandlers);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (aDocument && HasFlag(NODE_HAS_ID) && !GetBindingParent()) {
+    aDocument->AddToIdTable(this, DoGetID());
+  }
+
+  return NS_OK;
+}
+
+void
+nsXMLElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
+{
+  RemoveFromIdTable();
+
+  return nsGenericElement::UnbindFromTree(aDeep, aNullParent);
+}
--- a/content/xml/content/src/nsXMLElement.h
+++ b/content/xml/content/src/nsXMLElement.h
@@ -57,11 +57,25 @@ public:
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE(nsGenericElement::)
 
   // nsIDOMElement
   NS_FORWARD_NSIDOMELEMENT(nsGenericElement::)
 
   // nsINode interface methods
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  // nsIContent interface methods
+  virtual nsIAtom *GetIDAttributeName() const;
+  virtual nsIAtom* DoGetID() const;
+  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent,
+                              PRBool aCompileEventHandlers);
+  virtual void UnbindFromTree(PRBool aDeep, PRBool aNullParent);
+  virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
+                             PRBool aNotify);
+  virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
+                                nsIAtom* aAttribute,
+                                const nsAString& aValue,
+                                nsAttrValue& aResult);
 };
 
 #endif // nsXMLElement_h___
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -267,17 +267,17 @@ nsXULElement::Create(nsXULPrototypeEleme
                      PRBool aIsScriptable)
 {
     nsXULElement *element = new nsXULElement(aNodeInfo);
     if (element) {
         NS_ADDREF(element);
 
         element->mPrototype = aPrototype;
         if (aPrototype->mHasIdAttribute) {
-            element->SetFlags(NODE_MAY_HAVE_ID);
+            element->SetFlags(NODE_HAS_ID);
         }
         if (aPrototype->mHasClassAttribute) {
             element->SetFlags(NODE_MAY_HAVE_CLASS);
         }
         if (aPrototype->mHasStyleAttribute) {
             element->SetFlags(NODE_MAY_HAVE_STYLE);
         }
 
@@ -869,22 +869,19 @@ nsXULElement::MaybeAddPopupListener(nsIA
 //
 
 nsresult
 nsXULElement::BindToTree(nsIDocument* aDocument,
                          nsIContent* aParent,
                          nsIContent* aBindingParent,
                          PRBool aCompileEventHandlers)
 {
-  // Calling the nsStyledElementBase method on purpose to skip over
-  // nsStyledElement, since we don't want the style attribute
-  // reparsing it does.
-  nsresult rv = nsStyledElementBase::BindToTree(aDocument, aParent,
-                                                aBindingParent,
-                                                aCompileEventHandlers);
+  nsresult rv = nsStyledElement::BindToTree(aDocument, aParent,
+                                            aBindingParent,
+                                            aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument) {
       NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                    "Missing a script blocker!");
       // We're in a document now.  Kick off the frame load.
       LoadSrc();
   }
@@ -1301,16 +1298,23 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpa
         FindPrototypeAttribute(aNameSpaceID, aName);
     if (protoattr) {
         // We've got an attribute on the prototype, so we need to
         // fully fault and remove the local copy.
         rv = MakeHeavyweight();
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
+    PRBool isId = PR_FALSE;
+    if (aName == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) {
+      // Have to do this before clearing flag. See RemoveFromIdTable
+      RemoveFromIdTable();
+      isId = PR_TRUE;
+    }
+
     PRInt32 index = mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID);
     if (index < 0) {
         NS_ASSERTION(!protoattr, "we used to have a protoattr, we should now "
                                  "have a normal one");
 
         return NS_OK;
     }
 
@@ -1354,16 +1358,20 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpa
     // the attribute from the attribute map even though the attribute is still
     // on the element
     // https://bugzilla.mozilla.org/show_bug.cgi?id=296205
 
     // Deal with modification of magical attributes that side-effect
     // other things.
     // XXX Know how to remove POPUP event listeners when an attribute is unset?
 
+    if (isId) {
+        UnsetFlags(NODE_HAS_ID);
+    }
+
     if (aNameSpaceID == kNameSpaceID_None) {
         if (aName == nsGkAtoms::hidechrome &&
             mNodeInfo->Equals(nsGkAtoms::window)) {
             HideWindowChrome(PR_FALSE);
         }
 
         if (doc && doc->GetRootElement() == this) {
             if ((aName == nsGkAtoms::activetitlebarcolor ||
@@ -1712,36 +1720,32 @@ nsXULElement::GetBuilder(nsIXULTemplateB
 
     return NS_OK;
 }
 
 
 //----------------------------------------------------------------------
 // Implementation methods
 
-/// XXX GetID must be defined here because we have proto attrs.
+// XXX DoGetID and DoGetClasses must be defined here because we have proto
+// attributes.
 nsIAtom*
-nsXULElement::GetID() const
+nsXULElement::DoGetID() const
 {
-    if (!HasFlag(NODE_MAY_HAVE_ID)) {
-        return nsnull;
-    }
-
-    const nsAttrValue* attrVal = FindLocalOrProtoAttr(kNameSpaceID_None, nsGkAtoms::id);
-
-    NS_ASSERTION(!attrVal ||
-                 attrVal->Type() == nsAttrValue::eAtom ||
-                 (attrVal->Type() == nsAttrValue::eString &&
-                  attrVal->GetStringValue().IsEmpty()),
-                 "unexpected attribute type");
-
-    if (attrVal && attrVal->Type() == nsAttrValue::eAtom) {
-        return attrVal->GetAtomValue();
-    }
-    return nsnull;
+    NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
+    const nsAttrValue* attr =
+        FindLocalOrProtoAttr(kNameSpaceID_None, nsGkAtoms::id);
+
+    // We need the nullcheck here because during unlink the prototype looses
+    // all of its attributes. We might want to change that.
+    // The nullcheck would also be needed if we make UnsetAttr use
+    // nsGenericElement::UnsetAttr as that calls out to various code between
+    // removing the attribute and clearing the NODE_HAS_ID flag.
+
+    return attr ? attr->GetAtomValue() : nsnull;
 }
 
 const nsAttrValue*
 nsXULElement::DoGetClasses() const
 {
     NS_ASSERTION(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call");
     return FindLocalOrProtoAttr(kNameSpaceID_None, nsGkAtoms::_class);
 }
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -523,17 +523,17 @@ public:
 
     virtual void PerformAccesskey(PRBool aKeyCausesActivation,
                                   PRBool aIsTrustedEvent);
     nsresult ClickWithInputSource(PRUint16 aInputSource);
 
     virtual nsIContent *GetBindingParent() const;
     virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
     virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull, PRBool aWithMouse = PR_FALSE);
-    virtual nsIAtom* GetID() const;
+    virtual nsIAtom* DoGetID() const;
     virtual const nsAttrValue* DoGetClasses() const;
 
     NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
     virtual nsICSSStyleRule* GetInlineStyleRule();
     virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                                 PRInt32 aModType) const;
     NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
--- a/content/xul/document/public/nsIXULDocument.h
+++ b/content/xul/document/public/nsIXULDocument.h
@@ -42,40 +42,32 @@
 #include "nsString.h"
 #include "nsCOMArray.h"
 
 class nsIXULTemplateBuilder;
 class nsIContent;
 class nsIScriptGlobalObjectOwner;
 
 
-// {E486EA2A-5B37-4107-905F-EE062FB4FF97}
+// 3e872e97-b678-418e-a7e3-41b8305d4e75
 #define NS_IXULDOCUMENT_IID \
-{ 0xe486ea2a, 0x5b37, 0x4107, \
-  { 0x90, 0x5f, 0xee, 0x06, 0x2f, 0xb4, 0xff, 0x97 }}
+{ 0x3e872e97, 0xb678, 0x418e, \
+  { 0xa7, 0xe3, 0x41, 0xb8, 0x30, 0x5d, 0x4e, 0x75 } }
 
 
 /*
  * An XUL-specific extension to nsIDocument. Includes methods for
  * setting the root resource of the document content model, a factory
  * method for constructing the children of a node, etc.
  */
 class nsIXULDocument : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXULDOCUMENT_IID)
 
-  // The resource-to-element map is a one-to-many mapping of RDF
-  // resources to content elements.
-
-  /**
-   * Add an entry to the ID-to-element map.
-   */
-  NS_IMETHOD AddElementForID(nsIContent* aElement) = 0;
-
   /**
    * Get the elements for a particular resource --- all elements whose 'id'
    * or 'ref' is aID. The nsCOMArray will be truncated and filled in with
    * nsIContent pointers.
    */
   NS_IMETHOD GetElementsForID(const nsAString& aID, nsCOMArray<nsIContent>& aElements) = 0;
 
   /**
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -233,16 +233,17 @@ nsXULDocument::nsXULDocument(void)
 
     // NOTE! nsDocument::operator new() zeroes out all members, so don't
     // bother initializing members to 0.
 
     // Override the default in nsDocument
     mCharacterSet.AssignLiteral("UTF-8");
 
     mDefaultElementType = kNameSpaceID_XUL;
+    mIsXUL = PR_TRUE;
 
     mDelayFrameLoaderInitialization = PR_TRUE;
 }
 
 nsXULDocument::~nsXULDocument()
 {
     NS_ASSERTION(mNextSrcLoadWaiter == nsnull,
         "unreferenced document still waiting for script source to load?");
@@ -975,35 +976,28 @@ nsXULDocument::AttributeWillChange(nsIDo
     NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
 
     // XXXbz check aNameSpaceID, dammit!
     // See if we need to update our ref map.
     if (aAttribute == nsGkAtoms::ref ||
         (aAttribute == nsGkAtoms::id && !aContent->GetIDAttributeName())) {
         RemoveElementFromRefMap(aContent->AsElement());
     }
-    
-    nsXMLDocument::AttributeWillChange(aDocument, aContent, aNameSpaceID,
-                                       aAttribute, aModType);
 }
 
 void
 nsXULDocument::AttributeChanged(nsIDocument* aDocument,
                                 nsIContent* aElementContent, PRInt32 aNameSpaceID,
                                 nsIAtom* aAttribute, PRInt32 aModType)
 {
     NS_ASSERTION(aDocument == this, "unexpected doc");
 
     // XXXbz once we change AttributeChanged to take Element, we can nix this line
     Element* aElement = aElementContent->AsElement();
 
-    // Do this here so that all the exit paths below don't leave this undone
-    nsXMLDocument::AttributeChanged(aDocument, aElement, aNameSpaceID,
-                                    aAttribute, aModType);
-
     // XXXbz check aNameSpaceID, dammit!
     // See if we need to update our ref map.
     if (aAttribute == nsGkAtoms::ref ||
         (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) {
         AddElementToRefMap(aElement);
     }
     
     nsresult rv;
@@ -1095,66 +1089,46 @@ nsXULDocument::ContentAppended(nsIDocume
     NS_ASSERTION(aDocument == this, "unexpected doc");
     
     // Update our element map
     nsresult rv = NS_OK;
     for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv);
          cur = cur->GetNextSibling()) {
         rv = AddSubtreeToDocument(cur);
     }
-
-    nsXMLDocument::ContentAppended(aDocument, aContainer, aFirstNewContent,
-                                   aNewIndexInContainer);
 }
 
 void
 nsXULDocument::ContentInserted(nsIDocument* aDocument,
                                nsIContent* aContainer,
                                nsIContent* aChild,
                                PRInt32 aIndexInContainer)
 {
     NS_ASSERTION(aDocument == this, "unexpected doc");
 
     AddSubtreeToDocument(aChild);
-
-    nsXMLDocument::ContentInserted(aDocument, aContainer, aChild, aIndexInContainer);
 }
 
 void
 nsXULDocument::ContentRemoved(nsIDocument* aDocument,
                               nsIContent* aContainer,
                               nsIContent* aChild,
                               PRInt32 aIndexInContainer)
 {
     NS_ASSERTION(aDocument == this, "unexpected doc");
 
     RemoveSubtreeFromDocument(aChild);
-
-    nsXMLDocument::ContentRemoved(aDocument, aContainer, aChild, aIndexInContainer);
 }
 
 //----------------------------------------------------------------------
 //
 // nsIXULDocument interface
 //
 
 NS_IMETHODIMP
-nsXULDocument::AddElementForID(nsIContent* aElement)
-{
-    NS_PRECONDITION(aElement != nsnull, "null ptr");
-    if (! aElement)
-        return NS_ERROR_NULL_POINTER;
-    if (!aElement->IsElement())
-        return NS_ERROR_UNEXPECTED;
-
-    UpdateIdTableEntry(aElement->AsElement());
-    return NS_OK;
-}
-
-NS_IMETHODIMP
 nsXULDocument::GetElementsForID(const nsAString& aID,
                                 nsCOMArray<nsIContent>& aElements)
 {
     aElements.Clear();
 
     nsCOMPtr<nsIAtom> atom = do_GetAtom(aID);
     if (!atom)
         return NS_ERROR_OUT_OF_MEMORY;
@@ -1656,27 +1630,25 @@ NS_IMETHODIMP
 nsXULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
 {
     *aTracker = mCommandDispatcher;
     NS_IF_ADDREF(*aTracker);
     return NS_OK;
 }
 
 Element*
-nsXULDocument::GetElementById(const nsAString& aId, nsresult *aResult)
+nsXULDocument::GetElementById(const nsAString& aId)
 {
     nsCOMPtr<nsIAtom> atom(do_GetAtom(aId));
     if (!atom) {
-        *aResult = NS_ERROR_OUT_OF_MEMORY;
-
+        // This can only fail due OOM if the atom doesn't exist, in which
+        // case there couldn't possibly exist an entry for it.
         return nsnull;
     }
 
-    *aResult = NS_OK;
-
     if (!CheckGetElementByIdArg(atom))
         return nsnull;
 
     nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(atom);
     if (entry) {
         Element* element = entry->GetIdElement();
         if (element)
             return element;
@@ -1695,17 +1667,20 @@ nsXULDocument::AddElementToDocumentPre(E
 {
     // Do a bunch of work that's necessary when an element gets added
     // to the XUL Document.
     nsresult rv;
 
     // 1. Add the element to the resource-to-element map. Also add it to
     // the id map, since it seems this can be called when creating
     // elements from prototypes.
-    UpdateIdTableEntry(aElement);
+    nsIAtom* id = aElement->GetID();
+    if (id) {
+        AddToIdTable(aElement, id);
+    }
     rv = AddElementToRefMap(aElement);
     if (NS_FAILED(rv)) return rv;
 
     // 2. If the element is a 'command updater' (i.e., has a
     // "commandupdater='true'" attribute), then add the element to the
     // document's command dispatcher
     if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
                               nsGkAtoms::_true, eCaseMatters)) {
@@ -1830,17 +1805,20 @@ nsXULDocument::RemoveSubtreeFromDocument
         if (NS_FAILED(rv))
             return rv;
     }
 
     // 2. Remove the element from the resource-to-element map.
     // Also remove it from the id map, since we added it in
     // AddElementToDocumentPre().
     RemoveElementFromRefMap(aElement);
-    RemoveFromIdTable(aElement);
+    nsIAtom* id = aElement->GetID();
+    if (id) {
+        RemoveFromIdTable(aElement, id);
+    }
 
     // 3. If the element is a 'command updater', then remove the
     // element from the document's command dispatcher.
     if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
                               nsGkAtoms::_true, eCaseMatters)) {
         nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement);
         NS_ASSERTION(domelement != nsnull, "not a DOM element");
         if (! domelement)
@@ -2467,18 +2445,16 @@ nsXULDocument::PrepareToWalk()
     if (mState == eState_Master) {
         // Add the root element
         rv = CreateElementFromPrototype(proto, getter_AddRefs(root));
         if (NS_FAILED(rv)) return rv;
 
         rv = AppendChildTo(root, PR_FALSE);
         if (NS_FAILED(rv)) return rv;
         
-        // Add the root element to the XUL document's ID-to-element map.
-        UpdateIdTableEntry(root);
         rv = AddElementToRefMap(root);
         if (NS_FAILED(rv)) return rv;
 
         // Block onload until we've finished building the complete
         // document content model.
         BlockOnload();
     }
 
--- a/content/xul/document/src/nsXULDocument.h
+++ b/content/xul/document/src/nsXULDocument.h
@@ -143,17 +143,16 @@ public:
     // nsIMutationObserver interface
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
     NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
     NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
 
     // nsIXULDocument interface
-    NS_IMETHOD AddElementForID(nsIContent* aElement);
     NS_IMETHOD GetElementsForID(const nsAString& aID,
                                 nsCOMArray<nsIContent>& aElements);
 
     NS_IMETHOD GetScriptGlobalObjectOwner(nsIScriptGlobalObjectOwner** aGlobalOwner);
     NS_IMETHOD AddSubtreeToDocument(nsIContent* aContent);
     NS_IMETHOD RemoveSubtreeFromDocument(nsIContent* aContent);
     NS_IMETHOD SetTemplateBuilderFor(nsIContent* aContent,
                                      nsIXULTemplateBuilder* aBuilder);
@@ -165,18 +164,17 @@ public:
     // nsIDOMNode interface overrides
     NS_IMETHOD CloneNode(PRBool deep, nsIDOMNode **_retval);
 
     // nsDocument interface overrides
     NS_IMETHOD GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
     {
         return nsDocument::GetElementById(aId, aReturn);
     }
-    virtual mozilla::dom::Element* GetElementById(const nsAString & elementId,
-                                                  nsresult *aResult);
+    virtual mozilla::dom::Element* GetElementById(const nsAString & elementId);
 
     // nsIDOMXULDocument interface
     NS_DECL_NSIDOMXULDOCUMENT
 
     // nsIDOMNSDocument
     NS_IMETHOD GetContentType(nsAString& aContentType);
 
     // nsICSSLoaderObserver
--- a/content/xul/templates/src/nsXULContentBuilder.cpp
+++ b/content/xul/templates/src/nsXULContentBuilder.cpp
@@ -629,27 +629,16 @@ nsXULContentBuilder::BuildContentFromTem
             rv = aChild->GetId(id);
             if (NS_FAILED(rv))
                 return rv;
 
             rv = realKid->SetAttr(kNameSpaceID_None, nsGkAtoms::id, id, PR_FALSE);
             if (NS_FAILED(rv))
                 return rv;
 
-            if (! aNotify) {
-                // XUL document will watch us, and take care of making
-                // sure that we get added to or removed from the
-                // element map if aNotify is true. If not, we gotta do
-                // it ourselves. Yay.
-                nsCOMPtr<nsIXULDocument> xuldoc =
-                    do_QueryInterface(mRoot->GetDocument());
-                if (xuldoc)
-                    xuldoc->AddElementForID(realKid);
-            }
-
             // Set up the element's 'container' and 'empty' attributes.
             SetContainerAttrs(realKid, aChild, PR_TRUE, PR_FALSE);
         }
         else if (tag == nsGkAtoms::textnode &&
                  nameSpaceID == kNameSpaceID_XUL) {
             // <xul:text value="..."> is replaced by text of the
             // actual value of the 'rdf:resource' attribute for the
             // given node.