implement nsTObserverArray using nsTArray, clean up the interface, provide an nsAutoTObserverArray, and use it to reduce allocations in nsEventListenerManager. b=407442, r+sr=sicking, a=beltzner
authordwitte@stanford.edu
Tue, 18 Dec 2007 17:06:34 -0800
changeset 9457 a0853771eb6d423dffbbd6d5474df017b528d917
parent 9456 8e086a1825964bcbb5ed3700d0ff88011ef07b96
child 9458 216af8208e42309d5e2421c1f2c0fc14983aebcd
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbeltzner
bugs407442
milestone1.9b3pre
implement nsTObserverArray using nsTArray, clean up the interface, provide an nsAutoTObserverArray, and use it to reduce allocations in nsEventListenerManager. b=407442, r+sr=sicking, a=beltzner
content/base/public/nsIDocument.h
content/base/public/nsINode.h
content/base/public/nsPresShellIterator.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsGenericElement.cpp
content/events/src/nsEventListenerManager.cpp
content/events/src/nsEventListenerManager.h
layout/style/nsCSSLoader.cpp
layout/style/nsCSSLoader.h
modules/libpr0n/src/imgRequest.cpp
modules/libpr0n/src/imgRequest.h
xpcom/build/dlldeps.cpp
xpcom/glue/nsTArray.h
xpcom/glue/nsTObserverArray.cpp
xpcom/glue/nsTObserverArray.h
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -991,17 +991,17 @@ protected:
   // if this document is part of a multipart document,
   // the ID can be used to distinguish it from the other parts.
   PRUint32 mPartID;
   
   // Cycle collector generation in which we're certain that this document
   // won't be collected
   PRUint32 mMarkedCCGeneration;
 
-  nsTObserverArray<nsIPresShell> mPresShells;
+  nsTObserverArray<nsIPresShell*> mPresShells;
 
   nsCOMArray<nsINode> mSubtreeModifiedTargets;
   PRUint32            mSubtreeModifiedDepth;
 
 private:
   // JSObject cache. Only to be used for performance
   // optimizations. This will be set once this document is touched
   // from JS, and it will be unset once the JSObject is finalized.
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -559,17 +559,17 @@ public:
      * mFlagsOrSlots member, but these are used when the slots class
      * is allocated.
      */
     PtrBits mFlags;
 
     /**
      * A list of mutation observers
      */
-    nsTObserverArray<nsIMutationObserver> mMutationObservers;
+    nsTObserverArray<nsIMutationObserver*> mMutationObservers;
 
     /**
      * An object implementing nsIDOMNodeList for this content (childNodes)
      * @see nsIDOMNodeList
      * @see nsGenericHTMLElement::GetChildNodes
      *
      * MSVC 7 doesn't like this as an nsRefPtr
      */
--- a/content/base/public/nsPresShellIterator.h
+++ b/content/base/public/nsPresShellIterator.h
@@ -37,27 +37,27 @@
 
 #ifndef nsPresShellIterato_h___
 #define nsPresShellIterato_h___
 
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
 
 class nsPresShellIterator :
-  private nsTObserverArray<nsIPresShell>::ForwardIterator
+  private nsTObserverArray<nsIPresShell*>::ForwardIterator
 {
 public:
   nsPresShellIterator(nsIDocument* aDoc)
-  : nsTObserverArray<nsIPresShell>::ForwardIterator(aDoc->mPresShells),
+  : nsTObserverArray<nsIPresShell*>::ForwardIterator(aDoc->mPresShells),
     mDoc(aDoc) {}
 
   already_AddRefed<nsIPresShell> GetNextShell()
   {
     nsIPresShell* shell = nsnull;
-    if (!mDoc->ShellsAreHidden()) {
+    if (!mDoc->ShellsAreHidden() && HasMore()) {
       shell = GetNext();
       NS_IF_ADDREF(shell);
     }
     return shell;
   }
 private:
   static void* operator new(size_t) CPP_THROW_NEW { return 0; }
   static void operator delete(void*, size_t) {}
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1121,17 +1121,17 @@ nsDocument::Init()
 
   nsINode::nsSlots* slots = GetSlots();
   NS_ENSURE_TRUE(slots,NS_ERROR_OUT_OF_MEMORY);
 
   // Prepend self as mutation-observer whether we need it or not (some
   // subclasses currently do, other don't). This is because the code in
   // nsNodeUtils always notifies the first observer first, expecting the
   // first observer to be the document.
-  NS_ENSURE_TRUE(slots->mMutationObservers.PrependObserver(this),
+  NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(this),
                  NS_ERROR_OUT_OF_MEMORY);
 
 
   mOnloadBlocker = new nsOnloadBlocker();
   NS_ENSURE_TRUE(mOnloadBlocker, NS_ERROR_OUT_OF_MEMORY);
   
   NS_NewCSSLoader(this, &mCSSLoader);
   NS_ENSURE_TRUE(mCSSLoader, NS_ERROR_OUT_OF_MEMORY);
@@ -2040,34 +2040,34 @@ nsDocument::doCreateShell(nsPresContext*
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   rv = shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Note: we don't hold a ref to the shell (it holds a ref to us)
-  NS_ENSURE_TRUE(mPresShells.AppendObserverUnlessExists(shell),
+  NS_ENSURE_TRUE(mPresShells.AppendElementUnlessExists(shell),
                  NS_ERROR_OUT_OF_MEMORY);
   shell.swap(*aInstancePtrResult);
 
   return NS_OK;
 }
 
 PRBool
 nsDocument::DeleteShell(nsIPresShell* aShell)
 {
-  return mPresShells.RemoveObserver(aShell);
+  return mPresShells.RemoveElement(aShell);
 }
 
 
 nsIPresShell *
 nsDocument::GetPrimaryShell() const
 {
-  return mShellsAreHidden ? nsnull : mPresShells.SafeObserverAt(0);
+  return mShellsAreHidden ? nsnull : mPresShells.SafeElementAt(0, nsnull);
 }
 
 PR_STATIC_CALLBACK(void)
 SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
 {
   SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
 
   NS_RELEASE(e->mKey);
@@ -2649,30 +2649,30 @@ nsDocument::ScriptLoader()
 }
 
 // Note: We don't hold a reference to the document observer; we assume
 // that it has a live reference to the document.
 void
 nsDocument::AddObserver(nsIDocumentObserver* aObserver)
 {
   // The array makes sure the observer isn't already in the list
-  mObservers.AppendObserverUnlessExists(aObserver);
+  mObservers.AppendElementUnlessExists(aObserver);
   AddMutationObserver(aObserver);
 }
 
 PRBool
 nsDocument::RemoveObserver(nsIDocumentObserver* aObserver)
 {
   // If we're in the process of destroying the document (and we're
   // informing the observers of the destruction), don't remove the
   // observers from the list. This is not a big deal, since we
   // don't hold a live reference to the observers.
   if (!mInDestructor) {
     RemoveMutationObserver(aObserver);
-    return mObservers.RemoveObserver(aObserver);
+    return mObservers.RemoveElement(aObserver);
   }
 
   return mObservers.Contains(aObserver);
 }
 
 void
 nsDocument::BeginUpdate(nsUpdateType aUpdateType)
 {
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -736,17 +736,17 @@ protected:
   // will allow us to flush out any pending stuff from the sink even if
   // EndLoad() has already happened.
   nsWeakPtr mWeakSink;
 
   nsCOMArray<nsIStyleSheet> mStyleSheets;
   nsCOMArray<nsIStyleSheet> mCatalogSheets;
 
   // Array of observers
-  nsTObserverArray<nsIDocumentObserver> mObservers;
+  nsTObserverArray<nsIDocumentObserver*> mObservers;
 
   // The document's script global object, the object from which the
   // document can get its script context and scope. This is the
   // *inner* window object.
   nsCOMPtr<nsIScriptGlobalObject> mScriptGlobalObject;
 
   // If document is created for example using
   // document.implementation.createDocument(...), mScriptObject points to
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -315,26 +315,26 @@ nsINode::CreateSlots()
   return new (GetAllocator()) nsSlots(mFlagsOrSlots);
 }
 
 void
 nsINode::AddMutationObserver(nsIMutationObserver* aMutationObserver)
 {
   nsSlots* slots = GetSlots();
   if (slots) {
-    slots->mMutationObservers.AppendObserverUnlessExists(aMutationObserver);
+    slots->mMutationObservers.AppendElementUnlessExists(aMutationObserver);
   }
 }
 
 void
 nsINode::RemoveMutationObserver(nsIMutationObserver* aMutationObserver)
 {
   nsSlots* slots = GetExistingSlots();
   if (slots) {
-    slots->mMutationObservers.RemoveObserver(aMutationObserver);
+    slots->mMutationObservers.RemoveElement(aMutationObserver);
   }
 }
 
 PRBool
 nsINode::IsEditableInternal() const
 {
   if (HasFlag(NODE_IS_EDITABLE)) {
     // The node is in an editable contentEditable subtree.
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -364,20 +364,16 @@ nsEventListenerManager::~nsEventListener
     delete gEventIdTable;
     gEventIdTable = nsnull;
   }
 }
 
 nsresult
 nsEventListenerManager::RemoveAllListeners()
 {
-  PRInt32 count = mListeners.Count();
-  for (PRInt32 i = 0; i < count; i++) {
-    delete mListeners.FastObserverAt(i);
-  }
   mListeners.Clear();
   return NS_OK;
 }
 
 void
 nsEventListenerManager::Shutdown()
 {
   sAddListenerID = JSVAL_VOID;
@@ -392,19 +388,19 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
    NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsEventListenerManager, nsIEventListenerManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsEventListenerManager, nsIEventListenerManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEventListenerManager)
-  PRInt32 i, count = tmp->mListeners.Count();
+  PRInt32 i, count = tmp->mListeners.Length();
   for (i = 0; i < count; i++) {
-    cb.NoteXPCOMChild(tmp->mListeners.FastObserverAt(i)->mListener.get());
+    cb.NoteXPCOMChild(tmp->mListeners.ElementAt(i)->mListener.get());
   }  
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEventListenerManager)
   tmp->Disconnect();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
@@ -472,19 +468,19 @@ nsEventListenerManager::AddEventListener
       if (ifaceListener) {
         aTypeData = td;
         NS_RELEASE(ifaceListener);
       }
     }
   }
 
   nsListenerStruct* ls = nsnull;
-  PRInt32 count = mListeners.Count();
+  PRInt32 count = mListeners.Length();
   for (PRInt32 i = 0; i < count; i++) {
-    ls = mListeners.FastObserverAt(i);
+    ls = mListeners.ElementAt(i);
     if (ls->mListener == aListener && ls->mFlags == aFlags &&
         ls->mGroupFlags == group &&
         (EVENT_TYPE_EQUALS(ls, aType, aTypeAtom) ||
          EVENT_TYPE_DATA_EQUALS(aTypeData, ls->mTypeData))) {
       return NS_OK;
     }
   }
 
@@ -496,17 +492,17 @@ nsEventListenerManager::AddEventListener
 
   ls->mListener = aListener;
   ls->mEventType = aType;
   ls->mTypeAtom = aTypeAtom;
   ls->mFlags = aFlags;
   ls->mGroupFlags = group;
   ls->mHandlerIsString = PR_FALSE;
   ls->mTypeData = aTypeData;
-  mListeners.AppendObserver(ls);
+  mListeners.AppendElement(ls);
 
   // For mutation listeners, we need to update the global bit on the DOM window.
   // Otherwise we won't actually fire the mutation event.
   if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) {
     mMayHaveMutationListeners = PR_TRUE;
     // Go from our target to the nearest enclosing DOM window.
     nsCOMPtr<nsPIDOMWindow> window;
     nsCOMPtr<nsIDocument> document;
@@ -558,27 +554,26 @@ nsEventListenerManager::RemoveEventListe
     if (isSame) {
       group = NS_EVENT_FLAG_SYSTEM_EVENT;
     }
   }
 
   nsListenerStruct* ls = nsnull;
   aFlags &= ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
 
-  PRInt32 count = mListeners.Count();
+  PRInt32 count = mListeners.Length();
   for (PRInt32 i = 0; i < count; ++i) {
-    ls = mListeners.FastObserverAt(i);
+    ls = mListeners.ElementAt(i);
     if (ls->mListener == aListener &&
         ls->mGroupFlags == group &&
         ((ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) &&
         (EVENT_TYPE_EQUALS(ls, aType, aUserType) ||
          (!(ls->mEventType) &&
           EVENT_TYPE_DATA_EQUALS(ls->mTypeData, aTypeData)))) {
-      mListeners.RemoveObserverAt(i);
-      delete ls;
+      mListeners.RemoveElementAt(i);
       mNoListenerForEvent = NS_EVENT_TYPE_NULL;
       mNoListenerForEventAtom = nsnull;
       break;
     }
   }
 
   return NS_OK;
 }
@@ -640,19 +635,19 @@ nsEventListenerManager::RemoveEventListe
 
 nsListenerStruct*
 nsEventListenerManager::FindJSEventListener(PRUint32 aEventType,
                                             nsIAtom* aTypeAtom)
 {
   // Run through the listeners for this type and see if a script
   // listener is registered
   nsListenerStruct *ls;
-  PRInt32 count = mListeners.Count();
+  PRInt32 count = mListeners.Length();
   for (PRInt32 i = 0; i < count; ++i) {
-    ls = mListeners.FastObserverAt(i);
+    ls = mListeners.ElementAt(i);
     if (EVENT_TYPE_EQUALS(ls, aEventType, aTypeAtom) &&
         ls->mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) {
       return ls;
     }
   }
   return nsnull;
 }
 
@@ -838,18 +833,17 @@ nsEventListenerManager::AddScriptEventLi
 
 nsresult
 nsEventListenerManager::RemoveScriptEventListener(nsIAtom* aName)
 {
   PRUint32 eventType = nsContentUtils::GetEventId(aName);
   nsListenerStruct* ls = FindJSEventListener(eventType, aName);
 
   if (ls) {
-    mListeners.RemoveObserver(ls);
-    delete ls;
+    mListeners.RemoveElement(ls);
     mNoListenerForEvent = NS_EVENT_TYPE_NULL;
     mNoListenerForEventAtom = nsnull;
   }
 
   return NS_OK;
 }
 
 jsval
@@ -1101,17 +1095,17 @@ static const EventDispatchData* sLatestE
 
 nsresult
 nsEventListenerManager::HandleEvent(nsPresContext* aPresContext,
                                     nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
                                     nsISupports* aCurrentTarget,
                                     PRUint32 aFlags,
                                     nsEventStatus* aEventStatus)
 {
-  if (mListeners.Count() == 0 || aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
+  if (mListeners.IsEmpty() || aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
     return NS_OK;
   }
 
   // Check if we already know that there is no event listener for the event.
   if (mNoListenerForEvent == aEvent->message &&
       (mNoListenerForEvent != NS_USER_DEFINED_EVENT ||
        mNoListenerForEventAtom == aEvent->userType)) {
     return NS_OK;
@@ -1152,21 +1146,21 @@ nsEventListenerManager::HandleEvent(nsPr
      }
      typeData = nsnull;
      dispData = nsnull;
     }
   }
 
 found:
 
-  nsTObserverArray<nsListenerStruct>::EndLimitedIterator iter(mListeners);
+  nsAutoTObserverArray<nsAutoPtr<nsListenerStruct>, 2>::EndLimitedIterator iter(mListeners);
   nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
   PRBool hasListener = PR_FALSE;
-  nsListenerStruct* ls;
-  while ((ls = iter.GetNext())) {
+  while (iter.HasMore()) {
+    nsListenerStruct* ls = iter.GetNext();
     PRBool useTypeInterface =
       EVENT_TYPE_DATA_EQUALS(ls->mTypeData, typeData);
     PRBool useGenericInterface =
       (!useTypeInterface && ListenerCanHandle(ls, aEvent));
     // Don't fire the listener if it's been removed.
     // Check that the phase is same in event and event listener.
     // Handle only trusted events, except when listener permits untrusted events.
     if (useTypeInterface || useGenericInterface) {
@@ -1677,38 +1671,38 @@ nsEventListenerManager::GetCoordinatesFo
   }
 }
 
 NS_IMETHODIMP
 nsEventListenerManager::HasMutationListeners(PRBool* aListener)
 {
   *aListener = PR_FALSE;
   if (mMayHaveMutationListeners) {
-    PRInt32 count = mListeners.Count();
+    PRInt32 count = mListeners.Length();
     for (PRInt32 i = 0; i < count; ++i) {
-      nsListenerStruct* ls = mListeners.FastObserverAt(i);
+      nsListenerStruct* ls = mListeners.ElementAt(i);
       if (ls->mEventType >= NS_MUTATION_START &&
           ls->mEventType <= NS_MUTATION_END) {
         *aListener = PR_TRUE;
         break;
       }
     }
   }
 
   return NS_OK;
 }
 
 PRUint32
 nsEventListenerManager::MutationListenerBits()
 {
   PRUint32 bits = 0;
   if (mMayHaveMutationListeners) {
-    PRInt32 i, count = mListeners.Count();
+    PRInt32 i, count = mListeners.Length();
     for (i = 0; i < count; ++i) {
-      nsListenerStruct* ls = mListeners.FastObserverAt(i);
+      nsListenerStruct* ls = mListeners.ElementAt(i);
       if (ls->mEventType >= NS_MUTATION_START &&
           ls->mEventType <= NS_MUTATION_END) {
         if (ls->mEventType == NS_MUTATION_SUBTREEMODIFIED) {
           return kAllMutationBits;
         }
         bits |= MutationBitForEventType(ls->mEventType);
       }
     }
@@ -1734,33 +1728,33 @@ nsEventListenerManager::HasListenersFor(
        }
      }
      typeData = nsnull;
      dispData = nsnull;
     }
   }
 found:
 
-  PRInt32 i, count = mListeners.Count();
+  PRInt32 i, count = mListeners.Length();
   for (i = 0; i < count; ++i) {
-    nsListenerStruct* ls = mListeners.FastObserverAt(i);
+    nsListenerStruct* ls = mListeners.ElementAt(i);
     if (ls->mTypeAtom == atom ||
         EVENT_TYPE_DATA_EQUALS(ls->mTypeData, typeData)) {
       return PR_TRUE;
     }
   }
   return PR_FALSE;
 }
 
 PRBool
 nsEventListenerManager::HasUnloadListeners()
 {
-  PRInt32 count = mListeners.Count();
+  PRInt32 count = mListeners.Length();
   for (PRInt32 i = 0; i < count; ++i) {
-    nsListenerStruct* ls = mListeners.FastObserverAt(i);
+    nsListenerStruct* ls = mListeners.ElementAt(i);
     if (ls->mEventType == NS_PAGE_UNLOAD ||
         ls->mEventType == NS_BEFORE_PAGE_UNLOAD ||
         (ls->mTypeData && ls->mTypeData->iid &&
          ls->mTypeData->iid->Equals(NS_GET_IID(nsIDOMLoadListener)))) {
       return PR_TRUE;
     }
   }
   return PR_FALSE;
--- a/content/events/src/nsEventListenerManager.h
+++ b/content/events/src/nsEventListenerManager.h
@@ -44,17 +44,16 @@
 #include "nsAutoPtr.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOM3EventTarget.h"
 #include "nsHashtable.h"
 #include "nsIScriptContext.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsIDOMEvent;
-class nsVoidArray;
 class nsIAtom;
 struct EventTypeData;
 
 typedef struct {
   nsRefPtr<nsIDOMEventListener> mListener;
   PRUint32                      mEventType;
   nsCOMPtr<nsIAtom>             mTypeAtom;
   PRUint16                      mFlags;
@@ -184,22 +183,22 @@ protected:
   PRBool PrepareToUseCaretPosition(nsIWidget* aEventWidget,
                                    nsIPresShell* aShell,
                                    nsPoint& aTargetPt);
   void GetCoordinatesFor(nsIDOMElement *aCurrentEl, nsPresContext *aPresContext,
                          nsIPresShell *aPresShell, nsPoint& aTargetPt);
   nsresult GetDOM2EventGroup(nsIDOMEventGroup** aGroup);
   PRBool ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent);
 
-  nsTObserverArray<nsListenerStruct> mListeners;
-  nsISupports*                       mTarget;  //WEAK
-  PRUint32                           mMayHaveMutationListeners : 1;
+  nsAutoTObserverArray<nsAutoPtr<nsListenerStruct>, 2> mListeners;
+  nsISupports*                                         mTarget;  //WEAK
+  PRUint32                                             mMayHaveMutationListeners : 1;
   // These two member variables are used to cache the information
   // about the last event which was handled but for which event listener manager
   // didn't have event listeners.
-  PRUint32                           mNoListenerForEvent : 31;
-  nsCOMPtr<nsIAtom>                  mNoListenerForEventAtom;
+  PRUint32                                             mNoListenerForEvent : 31;
+  nsCOMPtr<nsIAtom>                                    mNoListenerForEventAtom;
 
-  static PRUint32                    mInstanceCount;
-  static jsval                       sAddListenerID;
+  static PRUint32                                      mInstanceCount;
+  static jsval                                         sAddListenerID;
 };
 
 #endif // nsEventListenerManager_h__
--- a/layout/style/nsCSSLoader.cpp
+++ b/layout/style/nsCSSLoader.cpp
@@ -1570,19 +1570,20 @@ CSSLoaderImpl::SheetComplete(SheetLoadDa
     NS_ASSERTION(data && data->mMustNotify, "How did this data get here?");
     if (data->mObserver) {
       LOG(("  Notifying observer 0x%x for data 0x%s.  wasAlternate: %d",
            data->mObserver.get(), data, data->mWasAlternate));
       data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate,
                                         aStatus);
     }
 
-    nsTObserverArray<nsICSSLoaderObserver>::ForwardIterator iter(mObservers);
+    nsTObserverArray< nsCOMPtr<nsICSSLoaderObserver> >::ForwardIterator iter(mObservers);
     nsCOMPtr<nsICSSLoaderObserver> obs;
-    while ((obs = iter.GetNext())) {
+    while (iter.HasMore()) {
+      obs = iter.GetNext();
       LOG(("  Notifying global observer 0x%x for data 0x%s.  wasAlternate: %d",
            obs.get(), data, data->mWasAlternate));
       obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus);
     }
   }
 
   if (mLoadingDatas.Count() == 0 && mPendingDatas.Count() > 0) {
     LOG(("  No more loading sheets; starting alternates"));
@@ -2320,30 +2321,27 @@ CSSLoaderImpl::HasPendingLoads()
     mPostedEvents.Length() != 0 ||
     mDatasToNotifyOn != 0;
 }
 
 NS_IMETHODIMP
 CSSLoaderImpl::AddObserver(nsICSSLoaderObserver* aObserver)
 {
   NS_PRECONDITION(aObserver, "Must have observer");
-  if (mObservers.AppendObserverUnlessExists(aObserver)) {
-    NS_ADDREF(aObserver);
+  if (mObservers.AppendElementUnlessExists(aObserver)) {
     return NS_OK;
   }
 
   return NS_ERROR_OUT_OF_MEMORY;
 }
 
 NS_IMETHODIMP_(void)
 CSSLoaderImpl::RemoveObserver(nsICSSLoaderObserver* aObserver)
 {
-  if (mObservers.RemoveObserver(aObserver)) {
-    NS_RELEASE(aObserver);
-  }
+  mObservers.RemoveElement(aObserver);
 }
 
 PR_STATIC_CALLBACK(PLDHashOperator)
 CollectLoadDatas(nsURIAndPrincipalHashKey *aKey,
                  SheetLoadData* &aData,
                  void* aClosure)
 {
   static_cast<CSSLoaderImpl::LoadDataArray*>(aClosure)->AppendElement(aData);
--- a/layout/style/nsCSSLoader.h
+++ b/layout/style/nsCSSLoader.h
@@ -500,12 +500,12 @@ private:
 
   // Number of datas still waiting to be notified on if we're notifying on a
   // whole bunch at once (e.g. in one of the stop methods).  This is used to
   // make sure that HasPendingLoads() won't return false until we're notifying
   // on the last data we're working with.
   PRUint32 mDatasToNotifyOn;
 
   // Our array of "global" observers
-  nsTObserverArray<nsICSSLoaderObserver> mObservers;
+  nsTObserverArray< nsCOMPtr<nsICSSLoaderObserver> > mObservers;
 };
 
 #endif // nsCSSLoader_h__
--- a/modules/libpr0n/src/imgRequest.cpp
+++ b/modules/libpr0n/src/imgRequest.cpp
@@ -125,25 +125,25 @@ nsresult imgRequest::Init(nsIURI *aURI,
   return NS_OK;
 }
 
 nsresult imgRequest::AddProxy(imgRequestProxy *proxy)
 {
   NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
 
-  return mObservers.AppendObserverUnlessExists(proxy) ?
+  return mObservers.AppendElementUnlessExists(proxy) ?
     NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBool aNotify)
 {
   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy);
 
-  mObservers.RemoveObserver(proxy);
+  mObservers.RemoveElement(proxy);
 
   /* Check mState below before we potentially call Cancel() below. Since
      Cancel() may result in OnStopRequest being called back before Cancel()
      returns, leaving mState in a different state then the one it was in at
      this point.
    */
 
   if (aNotify) {
@@ -328,19 +328,20 @@ void imgRequest::RemoveFromCache()
   if (mCacheEntry) {
     mCacheEntry->Doom();
     mCacheEntry = nsnull;
   }
 }
 
 PRBool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
 {
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
+  while (iter.HasMore()) {
+    proxy = iter.GetNext();
     if (proxy == aProxyToIgnore) {
       continue;
     }
     
     if (proxy->HasObserver()) {
       return PR_TRUE;
     }
   }
@@ -361,17 +362,17 @@ void imgRequest::AdjustPriority(imgReque
 {
   // only the first proxy is allowed to modify the priority of this image load.
   //
   // XXX(darin): this is probably not the most optimal algorithm as we may want
   // to increase the priority of requests that have a lot of proxies.  the key
   // concern though is that image loads remain lower priority than other pieces
   // of content such as link clicks, CSS, and JS.
   //
-  if (mObservers.SafeObserverAt(0) != proxy)
+  if (mObservers.SafeElementAt(0, nsnull) != proxy)
     return;
 
   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
   if (p)
     p->AdjustPriority(delta);
 }
 
 /** imgILoad methods **/
@@ -407,38 +408,36 @@ NS_IMETHODIMP imgRequest::GetIsMultiPart
 
 /* [noscript] void frameChanged (in imgIContainer container, in gfxIImageFrame newframe, in nsIntRect dirtyRect); */
 NS_IMETHODIMP imgRequest::FrameChanged(imgIContainer *container,
                                        gfxIImageFrame *newframe,
                                        nsIntRect * dirtyRect)
 {
   LOG_SCOPE(gImgLog, "imgRequest::FrameChanged");
 
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->FrameChanged(container, newframe, dirtyRect);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->FrameChanged(container, newframe, dirtyRect);
   }
 
   return NS_OK;
 }
 
 /** imgIDecoderObserver methods **/
 
 /* void onStartDecode (in imgIRequest request); */
 NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
 {
   LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
 
   mState |= onStartDecode;
 
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->OnStartDecode();
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnStartDecode();
   }
 
   /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
      indicates the beginning of a new decode.
      The cache entry's size therefore needs to be reset to 0 here.  If we do not do this,
      the code in imgRequest::OnStopFrame will continue to increase the data size cumulatively.
    */
   if (mCacheEntry)
@@ -460,51 +459,48 @@ NS_IMETHODIMP imgRequest::OnStartContain
 
   NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!");
   if (!image) return NS_ERROR_UNEXPECTED;
 
   mState |= onStartContainer;
 
   mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
 
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->OnStartContainer(image);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnStartContainer(image);
   }
 
   return NS_OK;
 }
 
 /* void onStartFrame (in imgIRequest request, in gfxIImageFrame frame); */
 NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request,
                                        gfxIImageFrame *frame)
 {
   LOG_SCOPE(gImgLog, "imgRequest::OnStartFrame");
 
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->OnStartFrame(frame);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnStartFrame(frame);
   }
 
   return NS_OK;
 }
 
 /* [noscript] void onDataAvailable (in imgIRequest request, in gfxIImageFrame frame, [const] in nsIntRect rect); */
 NS_IMETHODIMP imgRequest::OnDataAvailable(imgIRequest *request,
                                           gfxIImageFrame *frame,
                                           const nsIntRect * rect)
 {
   LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable");
 
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->OnDataAvailable(frame, rect);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnDataAvailable(frame, rect);
   }
 
   return NS_OK;
 }
 
 /* void onStopFrame (in imgIRequest request, in gfxIImageFrame frame); */
 NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
                                       gfxIImageFrame *frame)
@@ -521,37 +517,35 @@ NS_IMETHODIMP imgRequest::OnStopFrame(im
     mCacheEntry->GetDataSize(&cacheSize);
 
     PRUint32 imageSize = 0;
     frame->GetImageDataLength(&imageSize);
 
     mCacheEntry->SetDataSize(cacheSize + imageSize);
   }
 
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->OnStopFrame(frame);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnStopFrame(frame);
   }
 
   return NS_OK;
 }
 
 /* void onStopContainer (in imgIRequest request, in imgIContainer image); */
 NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
                                           imgIContainer *image)
 {
   LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
 
   mState |= onStopContainer;
 
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->OnStopContainer(image);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnStopContainer(image);
   }
 
   return NS_OK;
 }
 
 /* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */
 NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
                                        nsresult aStatus,
@@ -562,20 +556,19 @@ NS_IMETHODIMP imgRequest::OnStopDecode(i
   NS_ASSERTION(!(mState & onStopDecode), "OnStopDecode called multiple times.");
 
   mState |= onStopDecode;
 
   if (NS_FAILED(aStatus) && !(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL)) {
     mImageStatus |= imgIRequest::STATUS_ERROR;
   }
 
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), aStatusArg);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnStopDecode(GetResultFromImageStatus(mImageStatus), aStatusArg);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest,
                                         PRBool aLastPart)
 {
@@ -602,20 +595,19 @@ NS_IMETHODIMP imgRequest::OnStartRequest
      to onStartRequest. */
   mImageStatus = imgIRequest::STATUS_NONE;
   mState = onStartRequest;
 
   /* set our loading flag to true */
   mLoading = PR_TRUE;
 
   /* notify our kids */
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    proxy->OnStartRequest(aRequest, ctxt);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnStartRequest(aRequest, ctxt);
   }
 
   /* Get our principal */
   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   if (chan) {
     nsCOMPtr<nsIScriptSecurityManager> secMan =
       do_GetService("@mozilla.org/scriptsecuritymanager;1");
     if (secMan) {
@@ -734,23 +726,19 @@ NS_IMETHODIMP imgRequest::OnStopRequest(
 
   // if there was an error loading the image, (mState & onStopDecode) won't be true.
   // Send an onStopDecode message
   if (!(mState & onStopDecode)) {
     this->OnStopDecode(nsnull, status, nsnull);
   }
 
   /* notify the kids */
-  nsTObserverArray<imgRequestProxy>::ForwardIterator iter(mObservers);
-  imgRequestProxy* proxy;
-  while ((proxy = iter.GetNext())) {
-    /* calling OnStopRequest may result in the death of |proxy| so don't use the
-       pointer after this call.
-     */
-    proxy->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+  while (iter.HasMore()) {
+    iter.GetNext()->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
   }
 
   return NS_OK;
 }
 
 
 
 
--- a/modules/libpr0n/src/imgRequest.h
+++ b/modules/libpr0n/src/imgRequest.h
@@ -153,17 +153,17 @@ public:
 private:
   nsCOMPtr<nsIRequest> mRequest;
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<imgIContainer> mImage;
   nsCOMPtr<imgIDecoder> mDecoder;
   nsCOMPtr<nsIProperties> mProperties;
 
-  nsTObserverArray<imgRequestProxy> mObservers;
+  nsTObserverArray<imgRequestProxy*> mObservers;
 
   PRPackedBool mLoading;
   PRPackedBool mProcessing;
   PRPackedBool mHadLastPart;
   PRUint32 mNetworkStatus;
   PRUint32 mImageStatus;
   PRUint32 mState;
   nsCString mContentType;
--- a/xpcom/build/dlldeps.cpp
+++ b/xpcom/build/dlldeps.cpp
@@ -122,18 +122,18 @@ void XXXNeverCalled()
       array1.AppendElement(a);
       array2.InsertElementAt(b, 0);
       array2.InsertElementAt(c, 0);
       array1.AppendElements(array2);
     }
     {
       nsTObserverArray<PRBool> dummyObserverArray;
       PRBool a = PR_FALSE;
-      dummyObserverArray.AppendObserver(&a);
-      dummyObserverArray.RemoveObserver(&a);
+      dummyObserverArray.AppendElement(a);
+      dummyObserverArray.RemoveElement(a);
       dummyObserverArray.Clear();
     }
     nsStringHashSet();
     nsCStringHashSet();
     nsInt32HashSet();
     nsVoidHashSet();
     nsCheapStringSet();
     nsCheapInt32Set();
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -354,16 +354,36 @@ class nsTArray : public nsTArray_base {
     const elem_type& operator[](index_type i) const {
       return ElementAt(i);
     }
 
     //
     // Search methods
     //
 
+    // This method searches for the first element in this array that is equal
+    // to the given element.
+    // @param item   The item to search for.
+    // @param comp   The Comparator used to determine element equality.
+    // @return       PR_TRUE if the element was found.
+    template<class Item, class Comparator>
+    PRBool Contains(const Item& item, const Comparator& comp) const {
+      return IndexOf(item, 0, comp) != NoIndex;
+    }
+
+    // This method searches for the first element in this array that is equal
+    // to the given element.  This method assumes that 'operator==' is defined
+    // for elem_type.
+    // @param item   The item to search for.
+    // @return       PR_TRUE if the element was found.
+    template<class Item>
+    PRBool Contains(const Item& item) const {
+      return IndexOf(item) != NoIndex;
+    }
+
     // This method searches for the offset of the first element in this
     // array that is equal to the given element.
     // @param item   The item to search for.
     // @param start  The index to start from.
     // @param comp   The Comparator used to determine element equality.
     // @return       The index of the found element or NoIndex if not found.
     template<class Item, class Comparator>
     index_type IndexOf(const Item& item, index_type start,
@@ -730,9 +750,17 @@ class nsAutoTArray : public nsTArray<E> 
                    reinterpret_cast<Header*>(&mAutoBuf),
                    "GetAutoArrayBuffer needs to be fixed");
     }
 
   protected:
     char mAutoBuf[sizeof(Header) + N * sizeof(elem_type)];
 };
 
+// specialization for N = 0. this makes the inheritance model easier for
+// templated users of nsAutoTArray.
+template<class E>
+class nsAutoTArray<E, 0> : public nsTArray<E> {
+  public:
+    nsAutoTArray() {}
+};
+
 #endif  // nsTArray_h__
--- a/xpcom/glue/nsTObserverArray.cpp
+++ b/xpcom/glue/nsTObserverArray.cpp
@@ -15,16 +15,17 @@
  * The Original Code is Mozilla.org code.
  *
  * The Initial Developer of the Original Code is Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2006
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jonas Sicking <jonas@sicking.cc> (Original Author)
+ *   Daniel Witte <dwitte@stanford.edu>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -33,37 +34,31 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsTObserverArray.h"
 
 void
-nsTObserverArray_base::AdjustIterators(PRInt32 aModPos,
-                                       PRInt32 aAdjustment)
+nsTObserverArray_base::AdjustIterators(index_type aModPos,
+                                       diff_type  aAdjustment)
 {
   NS_PRECONDITION(aAdjustment == -1 || aAdjustment == 1,
                   "invalid adjustment");
   Iterator_base* iter = mIterators;
   while (iter) {
-    NS_ASSERTION(&(iter->mArray) == this, "wrong array");
-
     if (iter->mPosition > aModPos) {
       iter->mPosition += aAdjustment;
     }
     iter = iter->mNext;
   }
 }
 
 void
-nsTObserverArray_base::Clear()
+nsTObserverArray_base::ClearIterators()
 {
-  mObservers.Clear();
-
   Iterator_base* iter = mIterators;
   while (iter) {
-    NS_ASSERTION(&(iter->mArray) == this, "wrong array");
-
     iter->mPosition = 0;
     iter = iter->mNext;
   }
 }
--- a/xpcom/glue/nsTObserverArray.h
+++ b/xpcom/glue/nsTObserverArray.h
@@ -15,16 +15,17 @@
  * The Original Code is Mozilla.org code.
  *
  * The Initial Developer of the Original Code is Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2006
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jonas Sicking <jonas@sicking.cc> (Original Author)
+ *   Daniel Witte <dwitte@stanford.edu>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -33,247 +34,344 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsTObserverArray_h___
 #define nsTObserverArray_h___
 
-#include "nsVoidArray.h"
+#include "nsTArray.h"
 
 class NS_COM_GLUE nsTObserverArray_base {
-  public:
-    class Iterator_base;
-    friend class Iterator_base;
+  protected:
+    typedef PRUint32 index_type;
+    typedef PRUint32 size_type;
+    typedef PRInt32  diff_type;
 
     class Iterator_base {
       protected:
         friend class nsTObserverArray_base;
 
-        Iterator_base(PRInt32 aPosition, const nsTObserverArray_base& aArray)
+        Iterator_base(index_type aPosition, Iterator_base* aNext)
           : mPosition(aPosition),
-            mNext(aArray.mIterators),
-            mArray(aArray) {
-          aArray.mIterators = this;
-        }
-
-        ~Iterator_base() {
-          NS_ASSERTION(mArray.mIterators == this,
-                       "Iterators must currently be destroyed in opposite order "
-                       "from the construction order. It is suggested that you "
-                       "simply put them on the stack");
-          mArray.mIterators = mNext;
+            mNext(aNext) {
         }
 
-        // These functions exists solely to avoid having to make the
-        // subclasses into friends of nsTObserverArray_base
-        void* GetSafeElementAt(PRInt32 aIndex) {
-          return mArray.mObservers.SafeElementAt(aIndex);
-        }
-        void* FastElementAt(PRInt32 aIndex) {
-          return mArray.mObservers.FastElementAt(aIndex);
-        }
-
-        // The current position of the iterator. It's exact meaning differs
-        // depending on if the array is iterated forwards or backwards. See
-        // nsTObserverArray<T>::ForwardIterator and
-        // nsTObserverArray<T>::ReverseIterator
-        PRInt32 mPosition;
+        // The current position of the iterator. Its exact meaning differs
+        // depending on iterator. See nsTObserverArray<T>::ForwardIterator.
+        index_type mPosition;
 
         // The next iterator currently iterating the same array
         Iterator_base* mNext;
-
-        // The array we're iterating
-        const nsTObserverArray_base& mArray;
     };
 
-    /**
-     * Removes all observers and collapses all iterators to the beginning of
-     * the array. The result is that forward iterators will see all elements
-     * in the array, and backward iterators will not see any more elements.
-     */
-    void Clear();
-
-  protected:
     nsTObserverArray_base()
       : mIterators(nsnull) {
     }
 
     /**
      * Adjusts iterators after an element has been inserted or removed
      * from the array.
      * @param modPos     Position where elements were added or removed.
      * @param adjustment -1 if an element was removed, 1 if an element was
      *                   added.
      */
-    void AdjustIterators(PRInt32 aModPos, PRInt32 aAdjustment);
+    void AdjustIterators(index_type aModPos, diff_type aAdjustment);
+
+    /**
+     * Clears iterators when the array is destroyed.
+     */
+    void ClearIterators();
 
     mutable Iterator_base* mIterators;
-    nsVoidArray mObservers;
 };
 
 /**
  * An array of observers. Like a normal array, but supports iterators that are
  * stable even if the array is modified during iteration.
- * The template parameter is the type of observer the array will hold pointers
- * to.
+ * The template parameter T is the observer type the array will hold;
+ * N is the number of built-in storage slots that come with the array.
+ * NOTE: You probably want to use nsTObserverArray, unless you specifically
+ * want built-in storage. See below.
+ * @see nsTObserverArray, nsTArray
  */
 
-template<class T>
-class nsTObserverArray : public nsTObserverArray_base {
+template<class T, PRUint32 N>
+class nsAutoTObserverArray : protected nsTObserverArray_base {
   public:
+    typedef T           elem_type;
+    typedef nsTArray<T> array_type;
 
-    PRUint32 Count() const {
-      return mObservers.Count();
+    nsAutoTObserverArray() {
+    }
+
+    //
+    // Accessor methods
+    //
+
+    // @return The number of elements in the array.
+    size_type Length() const {
+      return mArray.Length();
     }
 
-    /**
-     * Adds an observer to the beginning of the array
-     * @param aObserver Observer to add
-     */
-    PRBool PrependObserver(T* aObserver) {
-      NS_PRECONDITION(!Contains(aObserver),
-                      "Don't prepend if the observer is already in the list");
+    // @return True if the array is empty or false otherwise.
+    PRBool IsEmpty() const {
+      return mArray.IsEmpty();
+    }
+
+    // This method provides direct access to the i'th element of the array.
+    // The given index must be within the array bounds.
+    // @param i  The index of an element in the array.
+    // @return   A reference to the i'th element of the array.
+    elem_type& ElementAt(index_type i) {
+      return mArray.ElementAt(i);
+    }
 
-      PRBool res = mObservers.InsertElementAt(aObserver, 0);
-      if (res) {
-        AdjustIterators(0, 1);
-      }
-      return res;
+    // Same as above, but readonly.
+    const elem_type& ElementAt(index_type i) const {
+      return mArray.ElementAt(i);
+    }
+
+    // This method provides direct access to the i'th element of the array in
+    // a bounds safe manner. If the requested index is out of bounds the
+    // provided default value is returned.
+    // @param i  The index of an element in the array.
+    // @param def The value to return if the index is out of bounds.
+    elem_type& SafeElementAt(index_type i, elem_type& def) {
+      return mArray.SafeElementAt(i, def);
+    }
+
+    // Same as above, but readonly.
+    const elem_type& SafeElementAt(index_type i, const elem_type& def) const {
+      return mArray.SafeElementAt(i, def);
     }
 
-    /**
-     * Adds an observer to the end of the array unless it already exists in
-     * the array.
-     * @param aObserver Observer to add
-     * @return True on success, false otherwise
-     */
-    PRBool AppendObserverUnlessExists(T* aObserver) {
-      return Contains(aObserver) || mObservers.AppendElement(aObserver);
+    //
+    // Search methods
+    //
+
+    // This method searches, starting from the beginning of the array,
+    // for the first element in this array that is equal to the given element.
+    // 'operator==' must be defined for elem_type.
+    // @param item   The item to search for.
+    // @return       PR_TRUE if the element was found.
+    template<class Item>
+    PRBool Contains(const Item& item) const {
+      return IndexOf(item) != array_type::NoIndex;
+    }
+
+    // This method searches for the offset of the first element in this
+    // array that is equal to the given element.
+    // 'operator==' must be defined for elem_type.
+    // @param item   The item to search for.
+    // @param start  The index to start from.
+    // @return       The index of the found element or NoIndex if not found.
+    template<class Item>
+    index_type IndexOf(const Item& item, index_type start = 0) const {
+      return mArray.IndexOf(item, start);
+    }
+
+    //
+    // Mutation methods
+    //
+
+    // Prepend an element to the array unless it already exists in the array.
+    // 'operator==' must be defined for elem_type.
+    // @param item   The item to prepend.
+    // @return       PR_TRUE if the element was found, or inserted successfully.
+    template<class Item>
+    PRBool PrependElementUnlessExists(const Item& item) {
+      return Contains(item) || mArray.InsertElementAt(0, item) != nsnull;
     }
 
-    /**
-     * Adds an observer to the end of the array.
-     * @param aObserver Observer to add
-     * @return True on success, false otherwise
-     */
-    PRBool AppendObserver(T* aObserver) {
-      return mObservers.AppendElement(aObserver);
+    // Append an element to the array.
+    // @param item   The item to append.
+    // @return A pointer to the newly appended element, or null on OOM.
+    template<class Item>
+    elem_type* AppendElement(const Item& item) {
+      return mArray.AppendElement(item);
+    }
+
+    // Same as above, but without copy-constructing. This is useful to avoid
+    // temporaries.
+    elem_type* AppendElement() {
+      return mArray.AppendElement();
     }
 
-    /**
-     * Removes an observer from the array
-     * @param aObserver Observer to remove
-     * @return True if observer was found and removed, false otherwise
-     */
-    PRBool RemoveObserver(T* aObserver) {
-      PRInt32 index = mObservers.IndexOf(aObserver);
-      if (index < 0) {
+    // Append an element to the array unless it already exists in the array.
+    // 'operator==' must be defined for elem_type.
+    // @param item   The item to append.
+    // @return       PR_TRUE if the element was found, or inserted successfully.
+    template<class Item>
+    PRBool AppendElementUnlessExists(const Item& item) {
+      return Contains(item) || AppendElement(item) != nsnull;
+    }
+
+    // Remove an element from the array.
+    // @param index  The index of the item to remove.
+    void RemoveElementAt(index_type index) {
+      NS_ASSERTION(index < mArray.Length(), "invalid index");
+      mArray.RemoveElementAt(index);
+      AdjustIterators(index, -1);
+    }
+
+    // This helper function combines IndexOf with RemoveElementAt to "search
+    // and destroy" the first element that is equal to the given element.
+    // 'operator==' must be defined for elem_type.
+    // @param item  The item to search for.
+    // @return PR_TRUE if the element was found and removed.
+    template<class Item>
+    PRBool RemoveElement(const Item& item) {
+      index_type index = mArray.IndexOf(item, 0);
+      if (index == array_type::NoIndex)
         return PR_FALSE;
-      }
 
-      mObservers.RemoveElementAt(index);
+      mArray.RemoveElementAt(index);
       AdjustIterators(index, -1);
-
       return PR_TRUE;
     }
 
-    /**
-     * Removes an observer from the array
-     * @param aIndex Index of observer to remove
-     */
-    void RemoveObserverAt(PRUint32 aIndex) {
-      if (aIndex < (PRUint32)mObservers.Count()) {
-        mObservers.RemoveElementAt(aIndex);
-        AdjustIterators(aIndex, -1);
-      }
-    }
-
-    PRBool Contains(T* aObserver) const {
-      return mObservers.IndexOf(aObserver) >= 0;
-    }
-
-    PRBool IsEmpty() const {
-      return mObservers.Count() == 0;
-    }
-
-    T* SafeObserverAt(PRInt32 aIndex) const {
-      return static_cast<T*>(mObservers.SafeElementAt(aIndex));
-    }
-
-    T* FastObserverAt(PRInt32 aIndex) const {
-      return static_cast<T*>(mObservers.FastElementAt(aIndex));
+    // Removes all observers and collapses all iterators to the beginning of
+    // the array. The result is that forward iterators will see all elements
+    // in the array.
+    void Clear() {
+      mArray.Clear();
+      ClearIterators();
     }
 
-    /**
-     * Iterators
-     */
+    //
+    // Iterators
+    //
+
+    // Base class for iterators. Do not use this directly.
+    class Iterator : public Iterator_base {
+      protected:
+        friend class nsAutoTObserverArray;
+        typedef nsAutoTObserverArray<T, N> array_type;
 
-    // Iterates the array forward from beginning to end.
-    // mPosition points to the element that will be returned on next call
-    // to GetNext
-    class ForwardIterator : public nsTObserverArray_base::Iterator_base {
-      public:
-        ForwardIterator(const nsTObserverArray<T>& aArray)
-          : Iterator_base(0, aArray) {
+        Iterator(index_type aPosition, const array_type& aArray)
+          : Iterator_base(aPosition, aArray.mIterators),
+            mArray(const_cast<array_type&>(aArray)) {
+          aArray.mIterators = this;
         }
-        ForwardIterator(const nsTObserverArray<T>& aArray, PRInt32 aPos)
-          : Iterator_base(aPos, aArray) {
+
+        ~Iterator() {
+          NS_ASSERTION(mArray.mIterators == this,
+                       "Iterators must currently be destroyed in opposite order "
+                       "from the construction order. It is suggested that you "
+                       "simply put them on the stack");
+          mArray.mIterators = mNext;
         }
 
-        PRBool operator <(const ForwardIterator& aOther) {
-          NS_ASSERTION(&mArray == &aOther.mArray,
-                       "not iterating the same array");
-          return mPosition < aOther.mPosition;
+        // The array we're iterating
+        array_type& mArray;
+    };
+
+    // Iterates the array forward from beginning to end. mPosition points
+    // to the element that will be returned on next call to GetNext.
+    // Elements:
+    // - prepended to the array during iteration *will not* be traversed
+    // - appended during iteration *will* be traversed
+    // - removed during iteration *will not* be traversed.
+    // @see EndLimitedIterator
+    class ForwardIterator : protected Iterator {
+      public:
+        typedef nsAutoTObserverArray<T, N> array_type;
+        typedef Iterator                   base_type;
+
+        ForwardIterator(const array_type& aArray)
+          : Iterator(0, aArray) {
         }
 
-        /**
-         * Returns the next element and steps one step.
-         * Returns null if there are no more observers. Once null is returned
-         * the iterator becomes invalid and GetNext must not be called any more.
-         * @return The next observer.
-         */
-        T* GetNext() {
-          return static_cast<T*>(GetSafeElementAt(mPosition++));
+        ForwardIterator(const array_type& aArray, index_type aPos)
+          : Iterator(aPos, aArray) {
+        }
+
+        PRBool operator <(const ForwardIterator& aOther) const {
+          NS_ASSERTION(&this->mArray == &aOther.mArray,
+                       "not iterating the same array");
+          return base_type::mPosition < aOther.mPosition;
+        }
+
+        // Returns PR_TRUE if there are more elements to iterate.
+        // This must precede a call to GetNext(). If PR_FALSE is
+        // returned, GetNext() must not be called.
+        PRBool HasMore() const {
+          return base_type::mPosition < base_type::mArray.Length();
+        }
+
+        // Returns the next element and steps one step. This must
+        // be preceded by a call to HasMore().
+        // @return The next observer.
+        elem_type& GetNext() {
+          NS_ASSERTION(HasMore(), "iterating beyond end of array");
+          return base_type::mArray.ElementAt(base_type::mPosition++);
         }
     };
 
     // EndLimitedIterator works like ForwardIterator, but will not iterate new
-    // observers added to the array after the iterator was created.
-    class EndLimitedIterator : private ForwardIterator {
+    // observers appended to the array after the iterator was created.
+    class EndLimitedIterator : protected ForwardIterator {
       public:
-        typedef typename nsTObserverArray<T>::ForwardIterator base_type;
+        typedef nsAutoTObserverArray<T, N> array_type;
+        typedef Iterator                   base_type;
 
-        EndLimitedIterator(const nsTObserverArray<T>& aArray)
+        EndLimitedIterator(const array_type& aArray)
           : ForwardIterator(aArray),
-            mEnd(aArray, aArray.Count()) {
+            mEnd(aArray, aArray.Length()) {
         }
 
-        /**
-         * Returns the next element and steps one step.
-         * Returns null if there are no more observers. Once null is returned
-         * the iterator becomes invalid and GetNext must not be called any more.
-         * @return The next observer.
-         */
-        T* GetNext() {
-          return (*this < mEnd) ?
-                 static_cast<T*>(FastElementAt(base_type::mPosition++)) :
-                 nsnull;
+        // Returns PR_TRUE if there are more elements to iterate.
+        // This must precede a call to GetNext(). If PR_FALSE is
+        // returned, GetNext() must not be called.
+        PRBool HasMore() const {
+          return *this < mEnd;
+        }
+
+        // Returns the next element and steps one step. This must
+        // be preceded by a call to HasMore().
+        // @return The next observer.
+        elem_type& GetNext() {
+          NS_ASSERTION(HasMore(), "iterating beyond end of array");
+          return base_type::mArray.ElementAt(base_type::mPosition++);
         }
 
       private:
         ForwardIterator mEnd;
     };
+
+  protected:
+    nsAutoTArray<T, N> mArray;
+};
+
+template<class T>
+class nsTObserverArray : public nsAutoTObserverArray<T, 0> {
+  public:
+    typedef nsAutoTObserverArray<T, 0>       base_type;
+    typedef nsTObserverArray_base::size_type size_type;
+
+    //
+    // Initialization methods
+    //
+
+    nsTObserverArray() {}
+
+    // Initialize this array and pre-allocate some number of elements.
+    explicit nsTObserverArray(size_type capacity) {
+      base_type::mArray.SetCapacity(capacity);
+    }
 };
 
 // XXXbz I wish I didn't have to pass in the observer type, but I
 // don't see a way to get it out of array_.
+// Note that this macro only works if the array holds pointers to XPCOM objects.
 #define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
   PR_BEGIN_MACRO                                                             \
-    nsTObserverArray<obstype_>::ForwardIterator iter_(array_);               \
+    nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_);             \
     nsCOMPtr<obstype_> obs_;                                                 \
-    while ((obs_ = iter_.GetNext())) {                                       \
+    while (iter_.HasMore()) {                                                 \
+      obs_ = iter_.GetNext();                                                \
       obs_ -> func_ params_ ;                                                \
     }                                                                        \
   PR_END_MACRO
 
 #endif // nsTObserverArray_h___