From c254a77eed328391b0e7f77ba1ad0be56c267f7f Mon Sep 17 00:00:00 2001
From: "aaronl%netscape.com" <aaronl%netscape.com>
Date: Wed, 30 Oct 2002 07:46:34 +0000
Subject: [PATCH] Bug 175842. Sending MSAA STATE_CHANGE event on old window,
 should be on new one. r=kyle, sr=jst, a=roc+moz

---
 .../src/base/nsAccessibilityService.cpp       |   1 +
 accessible/src/base/nsRootAccessible.cpp      | 307 +++++++++---------
 accessible/src/base/nsRootAccessible.h        |  17 +-
 .../src/html/nsHTMLIFrameRootAccessible.cpp   |  40 ++-
 .../src/html/nsHTMLIFrameRootAccessible.h     |  10 +
 5 files changed, 208 insertions(+), 167 deletions(-)

diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp
index 811c2c212949..4f80175f98be 100644
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -348,6 +348,7 @@ nsAccessibilityService::CreateRootAccessible(nsISupports* aPresContext, nsISuppo
 
   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(pcContainer));
   NS_ASSERTION(treeItem,"Error no tree item for pres context container!!!");
+  NS_ENSURE_TRUE(treeItem, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIDocShellTreeItem> treeItemParent;
   treeItem->GetParent(getter_AddRefs(treeItemParent));
diff --git a/accessible/src/base/nsRootAccessible.cpp b/accessible/src/base/nsRootAccessible.cpp
index 2efd05dc148c..a1520f96cecc 100644
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -92,7 +92,6 @@ NS_INTERFACE_MAP_BEGIN(nsRootAccessible)
   NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener)
   NS_INTERFACE_MAP_ENTRY(nsIDOMFormListener)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) 
   NS_INTERFACE_MAP_ENTRY(nsIScrollPositionListener) 
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormListener)
@@ -115,8 +114,10 @@ PRUint32 nsRootAccessible::gInstanceCount = 0;
 //-----------------------------------------------------
 nsRootAccessible::nsRootAccessible(nsIWeakReference* aShell):nsAccessible(nsnull,aShell), 
   nsDocAccessibleMixin(aShell), mListener(nsnull), 
+  mScrollWatchTimer(nsnull), mDocLoadTimer(nsnull), mWebProgress(nsnull),
   mAccService(do_GetService("@mozilla.org/accessibilityService;1")),
-  mBusy(eBusyStateUninitialized), mScrollPositionChangedTicks(0), mScrollablePresShells(nsnull)
+  mBusy(eBusyStateUninitialized), mIsNewDocument(PR_FALSE), 
+  mScrollPositionChangedTicks(0)
 {
   nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
   if (shell) {
@@ -141,8 +142,6 @@ nsRootAccessible::~nsRootAccessible()
 #endif
 
   RemoveAccessibleEventListener();
-  if (mScrollablePresShells)
-    delete mScrollablePresShells;
 }
 
   /* attribute wstring accName; */
@@ -241,38 +240,36 @@ NS_IMETHODIMP nsRootAccessible::GetAccValue(nsAString& aAccValue)
   return GetURL(aAccValue);
 }
 
-
-NS_IMETHODIMP nsRootAccessible::Notify(nsITimer *timer)
+void nsRootAccessible::DocLoadCallback(nsITimer *aTimer, void *aClosure)
 {
-  if (mScrollPositionChangedTicks) {
-    if (++mScrollPositionChangedTicks > 2) {
-      // Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
-      // We only want to fire accessibilty scroll event when scrolling stops or pauses
-      // Therefore, we wait for no scroll events to occur between 2 ticks of this timer
-      // That indicates a pause in scrolling, so we fire the accessibilty scroll event
-      nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mLastScrolledPresShell));
-      if (mListener && presShell) {
-        nsCOMPtr<nsIAccessible> docAccessible;
-        if (mPresShell == mLastScrolledPresShell)
-          docAccessible = this;  // Fast way, don't need to create new accessible to fire event on the root
-        else {
-          // Need to create accessible for document that had scrolling occur in it
-          nsCOMPtr<nsIDocument> doc;
-          presShell->GetDocument(getter_AddRefs(doc));
-          nsCOMPtr<nsIDOMNode> docNode(do_QueryInterface(doc));
-          mAccService->GetAccessibleFor(docNode, getter_AddRefs(docAccessible));
-        }
+  // Doc has finished loading, fire "load finished" event
+  // This path is only used if the doc was already finished loading
+  // when the RootAccessible was created. 
+  // Otherwise, ::OnStateChange() fires the event when doc is loaded.
 
-        if (docAccessible)
-          mListener->HandleEvent(nsIAccessibleEventListener::EVENT_SCROLLINGEND, docAccessible, nsnull);
-      }
-      mScrollPositionChangedTicks = 0;
-      mLastScrolledPresShell = nsnull;
-    }
-  }
-  return NS_OK;
+  nsRootAccessible *rootAcc = NS_REINTERPRET_CAST(nsRootAccessible*, aClosure);
+  if (rootAcc)
+    rootAcc->FireDocLoadFinished();
 }
-  
+
+void nsRootAccessible::ScrollTimerCallback(nsITimer *aTimer, void *aClosure)
+{
+  nsRootAccessible *rootAcc = NS_REINTERPRET_CAST(nsRootAccessible*, aClosure);
+
+  if (rootAcc && rootAcc->mScrollPositionChangedTicks && 
+      ++rootAcc->mScrollPositionChangedTicks > 2) {
+    // Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
+    // We only want to fire accessibilty scroll event when scrolling stops or pauses
+    // Therefore, we wait for no scroll events to occur between 2 ticks of this timer
+    // That indicates a pause in scrolling, so we fire the accessibilty scroll event
+    if (rootAcc->mListener)
+      rootAcc->mListener->HandleEvent(nsIAccessibleEventListener::EVENT_SCROLLINGEND, rootAcc, nsnull);
+    rootAcc->mScrollPositionChangedTicks = 0;
+    rootAcc->mScrollWatchTimer->Cancel();
+    rootAcc->mScrollWatchTimer = nsnull;
+  }
+}
+
 void nsRootAccessible::AddScrollListener(nsIPresShell *aPresShell)
 {
   nsCOMPtr<nsIViewManager> vm;
@@ -284,26 +281,8 @@ void nsRootAccessible::AddScrollListener(nsIPresShell *aPresShell)
   if (vm)
     vm->GetRootScrollableView(&scrollableView);
 
-  if (!scrollableView)
-    return;
-
-  if (!mScrollablePresShells)
-    mScrollablePresShells = new nsSupportsHashtable(SCROLL_HASH_START_SIZE, PR_TRUE);  // PR_TRUE = thread safe hash table
-
-  if (mScrollablePresShells) {
-    nsISupportsKey key(scrollableView);
-    nsCOMPtr<nsISupports> supports(dont_AddRef(NS_STATIC_CAST(nsISupports*, mScrollablePresShells->Get(&key))));
-    if (supports)  // This scroll view is already being listened to, remove it. We will re-add it w/ current pres shell
-      RemoveScrollListener(aPresShell);
-
-    // Add to hash table, so we can retrieve correct pres shell later
-    nsCOMPtr<nsIWeakReference> weakShell;
-    weakShell = do_GetWeakReference(aPresShell);
-    mScrollablePresShells->Put(&key, weakShell);
-
-    // Add listener
+  if (scrollableView)
     scrollableView->AddScrollPositionListener(NS_STATIC_CAST(nsIScrollPositionListener *, this));
-  }
 }
 
 void nsRootAccessible::RemoveScrollListener(nsIPresShell *aPresShell)
@@ -319,34 +298,6 @@ void nsRootAccessible::RemoveScrollListener(nsIPresShell *aPresShell)
 
   if (scrollableView)
     scrollableView->RemoveScrollPositionListener(NS_STATIC_CAST(nsIScrollPositionListener *, this));
-
-  if (mScrollablePresShells) {
-    nsISupportsKey key(scrollableView);
-    nsCOMPtr<nsISupports> supp;
-    mScrollablePresShells->Remove(&key, getter_AddRefs(supp));
-  }
-}
-
-PRBool PR_CALLBACK
-RemoveScrollListenerEnum(nsHashKey *aKey, void *aData, void* aClosure)
-{
-  nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(NS_STATIC_CAST(nsIWeakReference*, aData)));
-
-  // If presShell gone, then our nsIScrollPositionListener will also already be gone
-  if (presShell) {
-    nsCOMPtr<nsIViewManager> vm;
-    if (presShell)
-      presShell->GetViewManager(getter_AddRefs(vm));
-
-    nsIScrollableView* scrollableView = nsnull;
-    if (vm)
-      vm->GetRootScrollableView(&scrollableView);
-
-    if (scrollableView)
-      scrollableView->RemoveScrollPositionListener(NS_STATIC_CAST(nsIScrollPositionListener *, aClosure));
-  }
-
-  return PR_TRUE;
 }
 
 NS_IMETHODIMP nsRootAccessible::ScrollPositionWillChange(nsIScrollableView *aView, nscoord aX, nscoord aY)
@@ -357,29 +308,21 @@ NS_IMETHODIMP nsRootAccessible::ScrollPositionWillChange(nsIScrollableView *aVie
 NS_IMETHODIMP nsRootAccessible::ScrollPositionDidChange(nsIScrollableView *aScrollableView, nscoord aX, nscoord aY)
 {
   if (mListener) {
-    if (!mScrollablePresShells)
-      return NS_OK;
-    nsISupportsKey key(aScrollableView);
-    nsCOMPtr<nsIWeakReference> weakShell(dont_AddRef(NS_STATIC_CAST(nsIWeakReference*, mScrollablePresShells->Get(&key))));
-    if (!weakShell)
-      return NS_OK;
-    if (mTimer && (mBusy != eBusyStateDone || (mScrollPositionChangedTicks && mLastScrolledPresShell != weakShell))) {
-      // We need to say finish up our current time work and start a new timer
-      // either because we haven't yet fired our finished loading event, or 
-      // a scroll event from another pres shell is still waiting to be fired
-      Notify(mTimer);
-      mTimer->Cancel();
-    }
     // Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes, 
     // then the ::Notify() method will fire the accessibility event for scroll position changes
-    nsresult rv;
-    mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-    if (NS_SUCCEEDED(rv)) {
-      const PRUint32 kScrollPosCheckWait = 50;
-      mTimer->InitWithCallback(NS_STATIC_CAST(nsITimerCallback*, this), kScrollPosCheckWait, nsITimer::TYPE_REPEATING_SLACK);
+    const PRUint32 kScrollPosCheckWait = 50;
+    if (mScrollWatchTimer) {
+      mScrollWatchTimer->SetDelay(kScrollPosCheckWait);  // Create new timer, to avoid leaks
+    }
+    else {
+      mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
+      if (mScrollWatchTimer) {
+        mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
+                                                kScrollPosCheckWait, 
+                                                nsITimer::TYPE_REPEATING_SLACK);
+      }
     }
     mScrollPositionChangedTicks = 1;
-    mLastScrolledPresShell = weakShell;
   }
   return NS_OK;
 }
@@ -430,24 +373,7 @@ NS_IMETHODIMP nsRootAccessible::AddAccessibleEventListener(nsIAccessibleEventLis
     rv = target->AddEventListener(NS_LITERAL_STRING("DOMMenuBarInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
     
-    // Set up web progress listener - we need to know when page loading is finished
-    // That way we can send the STATE_CHANGE events for the MSAA root "pane" object (ROLE_PANE),
-    // and change the STATE_BUSY bit flag
-    nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mPresShell));
-    if (presShell) {
-      nsCOMPtr<nsIPresContext> context; 
-      presShell->GetPresContext(getter_AddRefs(context));
-      if (context) { 
-        nsCOMPtr<nsISupports> container; context->GetContainer(getter_AddRefs(container));
-        nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
-        if (docShell) {
-          mWebProgress = do_GetInterface(docShell);
-          mWebProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION | 
-                                                  nsIWebProgress::NOTIFY_STATE_DOCUMENT);
-        }
-      }
-    }
-    NS_ASSERTION(mWebProgress, "Could not get nsIWebProgress for nsRootAccessible");
+    AddContentDocListeners();
   }
 
   if (!mCaretAccessible && mListener)
@@ -474,18 +400,17 @@ NS_IMETHODIMP nsRootAccessible::RemoveAccessibleEventListener()
       target->RemoveEventListener(NS_LITERAL_STRING("ListitemStateChange"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE);
     }
  
-    if (mTimer) {
-      mTimer->Cancel();
-      mTimer = nsnull;
+    if (mScrollWatchTimer) {
+      mScrollWatchTimer->Cancel();
+      mScrollWatchTimer = nsnull;
     }
 
-    if (mWebProgress) {
-      mWebProgress->RemoveProgressListener(this);
-      mWebProgress = nsnull;
+    if (mDocLoadTimer) {
+      mDocLoadTimer->Cancel();
+      mDocLoadTimer = nsnull;
     }
 
-    if (mScrollablePresShells)
-      mScrollablePresShells->Enumerate(RemoveScrollListenerEnum, NS_STATIC_CAST(nsIScrollPositionListener*, this));
+    RemoveContentDocListeners();
 
     mListener = nsnull;
   }
@@ -809,11 +734,94 @@ NS_IMETHODIMP nsRootAccessible::GetDocument(nsIDocument **doc)
   return nsDocAccessibleMixin::GetDocument(doc);
 }
 
-NS_IMETHODIMP nsRootAccessible::OnStateChange(nsIWebProgress *aWebProgress,
-  nsIRequest *aRequest, PRUint32 aStateFlags, nsresult aStatus)
+void nsRootAccessible::RemoveContentDocListeners()
 {
-  if ((aStateFlags & STATE_IS_DOCUMENT) && (aStateFlags & STATE_STOP)) {
-    // Doc is ready!
+  // Remove listeners associated with content documents
+
+  // Remove web progress listener
+  if (mWebProgress) {
+    mWebProgress->RemoveProgressListener(this);
+    mWebProgress = nsnull;
+  }
+
+  // Remove scroll position listener
+  nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mPresShell));
+  RemoveScrollListener(presShell);
+}
+
+void nsRootAccessible::AddContentDocListeners()
+{
+  // 1) Set up scroll position listener
+  // 2) Set up web progress listener - we need to know 
+  //    when page loading is finished
+  //    That way we can send the STATE_CHANGE events for 
+  //    the MSAA root "pane" object (ROLE_PANE),
+  //    and change the STATE_BUSY bit flag
+  //    Do this only for top level content documents
+
+  nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mPresShell));
+  if (!presShell)
+    return;
+
+  AddScrollListener(presShell);
+
+  nsCOMPtr<nsISupports> container;
+  mDocument->GetContainer(getter_AddRefs(container));
+
+  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
+  if (!docShellTreeItem)
+    return;
+
+  // Make sure we're a content docshell
+  // We don't want to listen to chrome progress
+  PRInt32 itemType;
+  docShellTreeItem->GetItemType(&itemType);
+
+  if (itemType != nsIDocShellTreeItem::typeContent)
+    return;
+
+  // Make sure we're the top content doc shell 
+  // We don't want to listen to iframe progress
+  nsCOMPtr<nsIDocShellTreeItem> topOfContentTree;
+  docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(topOfContentTree));
+  if (topOfContentTree != docShellTreeItem)
+    return;
+  
+  nsCOMPtr<nsIPresContext> context; 
+  presShell->GetPresContext(getter_AddRefs(context));
+  if (!context)
+    return;
+
+  mWebProgress = do_GetInterface(docShellTreeItem);
+  if (!mWebProgress)
+    return;
+
+  mWebProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION | 
+                                    nsIWebProgress::NOTIFY_STATE_DOCUMENT);
+
+
+  mIsNewDocument = PR_TRUE;
+  mBusy = eBusyStateLoading;
+  PRBool isLoading;
+
+  mWebProgress->GetIsLoadingDocument(&isLoading);
+  if (!isLoading) {
+    // If already loaded, fire "done loading" event after short timeout
+    // If we fired the event here, we'd get reentrancy problems
+    // Otherwise it will be fired from OnStateChange when the load is done
+    mDocLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
+    if (mDocLoadTimer) {
+      mDocLoadTimer->InitWithFuncCallback(DocLoadCallback, this, 1,
+                                          nsITimer::TYPE_ONE_SHOT);
+    }
+  }
+}
+
+void nsRootAccessible::FireDocLoadFinished()
+{
+  if (mIsNewDocument) {
+    mIsNewDocument = PR_FALSE;
+
     if (mBusy != eBusyStateDone) {
       mBusy = eBusyStateDone;
 #ifndef MOZ_ACCESSIBILITY_ATK
@@ -821,21 +829,14 @@ NS_IMETHODIMP nsRootAccessible::OnStateChange(nsIWebProgress *aWebProgress,
         mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, this, nsnull);
 #endif
     }
-
-    // Set up scroll position listener
-    nsCOMPtr<nsIDOMWindow> domWin;
-    aWebProgress->GetDOMWindow(getter_AddRefs(domWin));
-    if (domWin) {
-      nsCOMPtr<nsIDOMDocument> domDoc;
-      domWin->GetDocument(getter_AddRefs(domDoc));
-      nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
-      if (doc) {
-        nsCOMPtr<nsIPresShell> presShell;
-        doc->GetShellAt(0, getter_AddRefs(presShell));
-        AddScrollListener(presShell);
-      }
-    }
   }
+}
+
+NS_IMETHODIMP nsRootAccessible::OnStateChange(nsIWebProgress *aWebProgress,
+  nsIRequest *aRequest, PRUint32 aStateFlags, nsresult aStatus)
+{
+  if ((aStateFlags & STATE_IS_DOCUMENT) && (aStateFlags & STATE_STOP))
+    FireDocLoadFinished();   // Doc is ready!
 
   return NS_OK;
 }
@@ -854,8 +855,14 @@ NS_IMETHODIMP nsRootAccessible::OnLocationChange(nsIWebProgress *aWebProgress,
   nsIRequest *aRequest, nsIURI *location)
 {
   // Load has been verified, it will occur, about to commence
+
+  // We won't fire a "doc finished loading" event on this nsRootAccessible 
+  // Instead we fire that on the new nsRootAccessible that is created for the new doc
+  mIsNewDocument = PR_FALSE;   // We're a doc that's going away
+
   if (mBusy != eBusyStateLoading) {
     mBusy = eBusyStateLoading; 
+    // Fire a "new doc has started to load" event
 #ifndef MOZ_ACCESSIBILITY_ATK
     if (mListener)
       mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, this, nsnull);
@@ -868,20 +875,6 @@ NS_IMETHODIMP nsRootAccessible::OnLocationChange(nsIWebProgress *aWebProgress,
       mListener->HandleEvent(nsIAccessibleEventListener::EVENT_REORDER , this, (AccessibleEventData*)&childrenData);
     }
 #endif
-
-    // Document is going away, remove its scroll position listener
-    nsCOMPtr<nsIDOMWindow> domWin;
-    aWebProgress->GetDOMWindow(getter_AddRefs(domWin));
-    if (domWin) {
-      nsCOMPtr<nsIDOMDocument> domDoc;
-      domWin->GetDocument(getter_AddRefs(domDoc));
-      nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
-      if (doc) {
-        nsCOMPtr<nsIPresShell> presShell;
-        doc->GetShellAt(0, getter_AddRefs(presShell));
-        RemoveScrollListener(presShell);
-      }
-    }
   }
 
   return NS_OK;
diff --git a/accessible/src/base/nsRootAccessible.h b/accessible/src/base/nsRootAccessible.h
index a7cc956395c3..16f89677c181 100644
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -80,7 +80,6 @@ class nsRootAccessible : public nsAccessible,
                          public nsIDOMFormListener,
                          public nsIDOMXULListener,
                          public nsIWebProgressListener,
-                         public nsITimerCallback, 
                          public nsIScrollPositionListener,
                          public nsSupportsWeakReference
 
@@ -132,9 +131,6 @@ class nsRootAccessible : public nsAccessible,
     NS_IMETHOD ScrollPositionWillChange(nsIScrollableView *aView, nscoord aX, nscoord aY);
     NS_IMETHOD ScrollPositionDidChange(nsIScrollableView *aView, nscoord aX, nscoord aY);
 
-    // ----- nsITimerCallback ------------------------------------
-    NS_DECL_NSITIMERCALLBACK
-
     NS_DECL_NSIACCESSIBLEDOCUMENT
     NS_DECL_NSIWEBPROGRESSLISTENER
 
@@ -145,7 +141,12 @@ class nsRootAccessible : public nsAccessible,
     void FireAccessibleFocusEvent(nsIAccessible *focusAccessible, nsIDOMNode *focusNode);
     void AddScrollListener(nsIPresShell *aPresShell);
     void RemoveScrollListener(nsIPresShell *aPresShell);
-    friend PRBool PR_CALLBACK RemoveScrollListenerEnum(nsHashKey *aKey, void *aData, void* aClosure);
+    void AddContentDocListeners();
+    void RemoveContentDocListeners();
+    void FireDocLoadFinished();
+
+    static void DocLoadCallback(nsITimer *aTimer, void *aClosure);
+    static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure);
 
     static PRUint32 gInstanceCount;
 
@@ -157,15 +158,15 @@ class nsRootAccessible : public nsAccessible,
 
     static nsIDOMNode * gLastFocusedNode; // we do our own refcounting for this
 
-    nsCOMPtr<nsITimer> mTimer;
+    nsCOMPtr<nsITimer> mScrollWatchTimer;
+    nsCOMPtr<nsITimer> mDocLoadTimer;
     nsCOMPtr<nsIWebProgress> mWebProgress;
     nsCOMPtr<nsIAccessibilityService> mAccService;
     EBusyState mBusy;
+    PRPackedBool mIsNewDocument;
 
     // Used for tracking scroll events
     PRUint32 mScrollPositionChangedTicks;
-    nsSupportsHashtable *mScrollablePresShells;
-    nsCOMPtr<nsIWeakReference> mLastScrolledPresShell;
     nsCOMPtr<nsIAccessibleCaret> mCaretAccessible;
 };
 
diff --git a/accessible/src/html/nsHTMLIFrameRootAccessible.cpp b/accessible/src/html/nsHTMLIFrameRootAccessible.cpp
index 7c84a3354f31..ef3ad3585c9e 100644
--- a/accessible/src/html/nsHTMLIFrameRootAccessible.cpp
+++ b/accessible/src/html/nsHTMLIFrameRootAccessible.cpp
@@ -51,7 +51,7 @@ NS_INTERFACE_MAP_END_INHERITING(nsRootAccessible)
 NS_IMPL_ADDREF_INHERITED(nsHTMLIFrameRootAccessible, nsRootAccessible);
 NS_IMPL_RELEASE_INHERITED(nsHTMLIFrameRootAccessible, nsRootAccessible);
 
-NS_IMPL_ISUPPORTS_INHERITED2(nsHTMLIFrameAccessible, nsBlockAccessible, nsIAccessibleDocument, nsIAccessibleHyperText)
+NS_IMPL_ISUPPORTS_INHERITED3(nsHTMLIFrameAccessible, nsBlockAccessible, nsIAccessibleDocument, nsIAccessibleHyperText, nsIAccessibleEventReceiver)
 
 nsHTMLIFrameAccessible::nsHTMLIFrameAccessible(nsIDOMNode* aNode, nsIAccessible* aRoot, nsIWeakReference* aShell, nsIDocument *aDoc):
   nsBlockAccessible(aNode, aShell), nsDocAccessibleMixin(aDoc), mRootAccessible(aRoot)
@@ -323,11 +323,25 @@ nsresult nsHTMLIFrameAccessible::GetLinkIndexFromAccNode(nsIAccessible *aAccNode
   return NS_ERROR_INVALID_ARG;
 }
 
+NS_IMETHODIMP nsHTMLIFrameAccessible::AddAccessibleEventListener(nsIAccessibleEventListener *aListener)
+{
+  nsCOMPtr<nsIAccessibleEventReceiver> rootReceiver(do_QueryInterface(mRootAccessible));
+  NS_ENSURE_TRUE(rootReceiver, NS_OK);
+  return rootReceiver->AddAccessibleEventListener(aListener);
+}
+
+NS_IMETHODIMP nsHTMLIFrameAccessible::RemoveAccessibleEventListener()
+{
+  nsCOMPtr<nsIAccessibleEventReceiver> rootReceiver(do_QueryInterface(mRootAccessible));
+  NS_ENSURE_TRUE(rootReceiver, NS_OK);
+  return rootReceiver->RemoveAccessibleEventListener();
+}
+
+
 //=============================//
 // nsHTMLIFrameRootAccessible  //
 //=============================//
 
-
 //-----------------------------------------------------
 // construction 
 //-----------------------------------------------------
@@ -393,3 +407,25 @@ NS_IMETHODIMP nsHTMLIFrameRootAccessible::GetAccPreviousSibling(nsIAccessible **
     Init();
   return mOuterAccessible->GetAccPreviousSibling(_retval);
 }
+
+/* void addAccessibleEventListener (in nsIAccessibleEventListener aListener); */
+NS_IMETHODIMP nsHTMLIFrameRootAccessible::AddAccessibleEventListener(nsIAccessibleEventListener *aListener)
+{
+  NS_ASSERTION(aListener, "Trying to add a null listener!");
+  if (!mListener) {
+    mListener = aListener;
+    AddContentDocListeners();
+  }
+  return NS_OK;
+}
+
+/* void removeAccessibleEventListener (); */
+NS_IMETHODIMP nsHTMLIFrameRootAccessible::RemoveAccessibleEventListener()
+{
+  if (mListener) {
+    RemoveContentDocListeners();
+    mListener = nsnull;
+  }
+  return NS_OK;
+}
+
diff --git a/accessible/src/html/nsHTMLIFrameRootAccessible.h b/accessible/src/html/nsHTMLIFrameRootAccessible.h
index 84199b74cde1..514dec31bc9f 100644
--- a/accessible/src/html/nsHTMLIFrameRootAccessible.h
+++ b/accessible/src/html/nsHTMLIFrameRootAccessible.h
@@ -44,6 +44,7 @@
 #include "nsAccessible.h"
 #include "nsIAccessibleDocument.h"
 #include "nsIAccessibleHyperText.h"
+#include "nsIAccessibleEventReceiver.h"
 
 class nsIWebShell;
 class nsIWeakReference;
@@ -51,6 +52,7 @@ class nsIWeakReference;
 class nsHTMLIFrameAccessible : public nsBlockAccessible, 
                                public nsIAccessibleDocument,
                                public nsIAccessibleHyperText,
+                               public nsIAccessibleEventReceiver,
                                public nsDocAccessibleMixin
 {
   NS_DECL_ISUPPORTS_INHERITED
@@ -68,6 +70,10 @@ class nsHTMLIFrameAccessible : public nsBlockAccessible,
     NS_IMETHOD GetAccRole(PRUint32 *aAccRole);
     NS_IMETHOD GetAccState(PRUint32 *aAccState);
 
+    // ----- nsIAccessibleEventReceiver -------------------
+    NS_IMETHOD AddAccessibleEventListener(nsIAccessibleEventListener *aListener);
+    NS_IMETHOD RemoveAccessibleEventListener();
+
   protected:
     nsCOMPtr<nsIAccessible> mRootAccessible;
 
@@ -99,6 +105,10 @@ class nsHTMLIFrameRootAccessible : public nsRootAccessible
     /* nsIAccessible getAccPreviousSibling (); */
     NS_IMETHOD GetAccPreviousSibling(nsIAccessible **_retval);
 
+    // ----- nsIAccessibleEventReceiver -------------------
+    NS_IMETHOD AddAccessibleEventListener(nsIAccessibleEventListener *aListener);
+    NS_IMETHOD RemoveAccessibleEventListener();
+
   protected:
     void Init();