Bug 523785 - Use NS_DispatchToMainThread instead of nsITimer, r=ginn, davidb, sr=smaug
authorAlexander Surkov <surkov.alexander@gmail.com>
Mon, 25 Jan 2010 23:08:08 +0800
changeset 37480 b8a1a64e36e0d6c72ddb1f0cf6bc3d4bbaef3f33
parent 37479 38e86af9675796a35fa8e9cc42b5aee339debe65
child 37481 0235fc257969646a5bcc3dbdc227103b40d9f090
push idunknown
push userunknown
push dateunknown
reviewersginn, davidb, smaug
bugs523785
milestone1.9.3a1pre
Bug 523785 - Use NS_DispatchToMainThread instead of nsITimer, r=ginn, davidb, sr=smaug
accessible/public/nsIAccessibilityService.idl
accessible/src/base/nsAccessNode.cpp
accessible/src/base/nsAccessNode.h
accessible/src/base/nsAccessibilityService.cpp
accessible/src/base/nsAccessibilityService.h
accessible/src/base/nsAccessible.cpp
accessible/src/base/nsAccessible.h
accessible/src/base/nsCoreUtils.h
accessible/src/base/nsRootAccessible.cpp
accessible/src/base/nsRootAccessible.h
--- a/accessible/public/nsIAccessibilityService.idl
+++ b/accessible/public/nsIAccessibilityService.idl
@@ -34,24 +34,22 @@
  * 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 "nsISupports.idl"
 #include "nsIAccessibleRetrieval.idl"
 
-interface nsIAccessibleEventListener;
 interface nsIDocument;
 interface nsIFrame;
 interface nsObjectFrame;
 interface nsIContent;
-interface nsITimer;
 
-[uuid(61098f48-4fcc-4b05-9cf3-c11b8efbe682)]
+[uuid(e0498def-1552-4763-8c47-6c6cc36c7aa0)]
 interface nsIAccessibilityService : nsIAccessibleRetrieval
 {
   nsIAccessible createOuterDocAccessible(in nsIDOMNode aNode);
   nsIAccessible createRootAccessible(in nsIPresShell aShell, in nsIDocument aDocument);
 
   nsIAccessible createHTML4ButtonAccessible(in nsIFrame aFrame);
   nsIAccessible createHyperTextAccessible(in nsIFrame aFrame);
   nsIAccessible createHTMLBRAccessible(in nsIFrame aFrame);
@@ -109,26 +107,16 @@ interface nsIAccessibilityService : nsIA
    * @param aPresShell   [in] the presShell where changes occured
    * @param aContent     [in] the affected DOM content
    * @param aChangeType  [in] the change type (see constants declared above)
    */
   void invalidateSubtreeFor(in nsIPresShell aPresShell, in nsIContent aContent,
                             in PRUint32 aChangeType);
 
   /**
-   *  An internal doc load event has occured. Handle the event and remove it from the list.
-   *  @param aTimer      The timer created to handle this doc load event
-   *  @param aClosure    The nsIWebProgress* for the load event
-   *  @param aEventType  The type of load event, one of: nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_START,
-   *                                                     nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE or
-   *                                                     nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED
-   */
-  void processDocLoadEvent(in nsITimer aTimer, in voidPtr aClosure, in PRUint32 aEventType);
-
-  /**
    * Notify accessibility that anchor jump has been accomplished to the given
    * target. Used by layout.
    */
   void notifyOfAnchorJumpTo(in nsIContent aTarget);
 
   /**
    * Fire accessible event of the given type for the given target.
    *
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -59,28 +59,26 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIFrame.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
-#include "nsITimer.h"
 #include "nsRootAccessible.h"
 #include "nsFocusManager.h"
 #include "nsIObserverService.h"
 
 /* For documentation of the accessibility architecture, 
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 nsIStringBundle *nsAccessNode::gStringBundle = 0;
 nsIStringBundle *nsAccessNode::gKeyStringBundle = 0;
-nsITimer *nsAccessNode::gDoCommandTimer = 0;
 nsIDOMNode *nsAccessNode::gLastFocusedNode = 0;
 #ifdef DEBUG
 PRBool nsAccessNode::gIsAccessibilityActive = PR_FALSE;
 #endif
 PRBool nsAccessNode::gIsCacheDisabled = PR_FALSE;
 PRBool nsAccessNode::gIsFormFillEnabled = PR_FALSE;
 nsAccessNodeHashtable nsAccessNode::gGlobalDocAccessibleCache;
 
@@ -305,17 +303,16 @@ void nsAccessNode::ShutdownXPAccessibili
   // Called by nsAccessibilityService::Shutdown()
   // which happens when xpcom is shutting down
   // at exit of program
 
   NS_ASSERTION(gIsAccessibilityActive, "Accessibility was shutdown already!");
 
   NS_IF_RELEASE(gStringBundle);
   NS_IF_RELEASE(gKeyStringBundle);
-  NS_IF_RELEASE(gDoCommandTimer);
   NS_IF_RELEASE(gLastFocusedNode);
 
   nsApplicationAccessibleWrap::Unload();
   ClearCache(gGlobalDocAccessibleCache);
 
   // Release gApplicationAccessible after everything else is shutdown
   // so we don't accidently create it again while tearing down root accessibles
   NS_IF_RELEASE(gApplicationAccessible);
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -58,17 +58,16 @@
 #include "nsInterfaceHashtable.h"
 #include "nsIAccessibilityService.h"
 
 class nsIPresShell;
 class nsPresContext;
 class nsIAccessibleDocument;
 class nsIFrame;
 class nsIDOMNodeList;
-class nsITimer;
 class nsRootAccessible;
 class nsApplicationAccessibleWrap;
 class nsIDocShellTreeItem;
 
 #define ACCESSIBLE_BUNDLE_URL "chrome://global-platform/locale/accessible.properties"
 #define PLATFORM_KEYS_BUNDLE_URL "chrome://global-platform/locale/platformKeys.properties"
 
 typedef nsInterfaceHashtable<nsVoidPtrHashKey, nsIAccessNode>
@@ -186,17 +185,17 @@ protected:
     /**
      * Notify global nsIObserver's that a11y is getting init'd or shutdown
      */
     static void NotifyA11yInitOrShutdown(PRBool aIsInit);
 
     // Static data, we do our own refcounting for our static data
     static nsIStringBundle *gStringBundle;
     static nsIStringBundle *gKeyStringBundle;
-    static nsITimer *gDoCommandTimer;
+
 #ifdef DEBUG
     static PRBool gIsAccessibilityActive;
 #endif
     static PRBool gIsCacheDisabled;
     static PRBool gIsFormFillEnabled;
 
     static nsAccessNodeHashtable gGlobalDocAccessibleCache;
 
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -156,29 +156,16 @@ nsAccessibilityService::Observe(nsISuppo
       do_GetService("@mozilla.org/observer-service;1");
     if (observerService) {
       observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     }
     nsCOMPtr<nsIWebProgress> progress(do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID));
     if (progress)
       progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this));
 
-    // Cancel and release load timers.
-    while (mLoadTimers.Count() > 0 ) {
-      nsCOMPtr<nsITimer> timer = mLoadTimers.ObjectAt(0);
-      void *closure = nsnull;
-      timer->GetClosure(&closure);
-      if (closure) {
-        nsIWebProgress *webProgress = static_cast<nsIWebProgress*>(closure);
-        NS_RELEASE(webProgress);  // Release nsIWebProgress for timer
-      }
-      timer->Cancel();
-      mLoadTimers.RemoveObjectAt(0);
-    }
-
     // Application is going to be closed, shutdown accessibility and mark
     // accessibility service as shutdown to prevent calls of its methods.
     // Don't null accessibility service static member at this point to be safe
     // if someone will try to operate with it.
 
     NS_ASSERTION(!gIsShutdown, "Accessibility was shutdown already");
 
     gIsShutdown = PR_TRUE;
@@ -201,72 +188,68 @@ NS_IMETHODIMP nsAccessibilityService::On
   
   nsCAutoString name;
   aRequest->GetName(name);
   if (name.EqualsLiteral("about:blank"))
     return NS_OK;
 
   if (NS_FAILED(aStatus) && (aStateFlags & STATE_START))
     return NS_OK;
- 
-  nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
-  if (!timer)
-    return NS_OK;
-  mLoadTimers.AppendObject(timer);
-  NS_ADDREF(aWebProgress);
 
-  if (aStateFlags & STATE_START)
-    timer->InitWithFuncCallback(StartLoadCallback, aWebProgress, 0,
-                                nsITimer::TYPE_ONE_SHOT);
-  else if (NS_SUCCEEDED(aStatus)) 
-    timer->InitWithFuncCallback(EndLoadCallback, aWebProgress, 0,
-                                nsITimer::TYPE_ONE_SHOT);
-  else // Failed end load
-    timer->InitWithFuncCallback(FailedLoadCallback, aWebProgress, 0,
-                                nsITimer::TYPE_ONE_SHOT);
+  if (aStateFlags & STATE_START) {
+    NS_DISPATCH_RUNNABLEMETHOD_ARG2(ProcessDocLoadEvent, this, aWebProgress,
+                                    nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_START)
+  } else if (NS_SUCCEEDED(aStatus)) {
+    NS_DISPATCH_RUNNABLEMETHOD_ARG2(ProcessDocLoadEvent, this, aWebProgress,
+                                    nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE)
+  } else { // Failed end load
+    NS_DISPATCH_RUNNABLEMETHOD_ARG2(ProcessDocLoadEvent, this, aWebProgress,
+                                    nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED)
+  }
+
   return NS_OK;
 }
 
-NS_IMETHODIMP nsAccessibilityService::ProcessDocLoadEvent(nsITimer *aTimer, void *aClosure, PRUint32 aEventType)
+void
+nsAccessibilityService::ProcessDocLoadEvent(nsIWebProgress *aWebProgress,
+                                            PRUint32 aEventType)
 {
+  if (gIsShutdown)
+    return;
+
   nsCOMPtr<nsIDOMWindow> domWindow;
-  nsIWebProgress *webProgress = static_cast<nsIWebProgress*>(aClosure);
-  webProgress->GetDOMWindow(getter_AddRefs(domWindow));
-  NS_RELEASE(webProgress);
-  mLoadTimers.RemoveObject(aTimer);
-  NS_ENSURE_STATE(domWindow);
+  aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
+  NS_ENSURE_TRUE(domWindow,);
 
   if (aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_START) {
     nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(domWindow));
     nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
-    NS_ENSURE_STATE(docShell);
+    NS_ENSURE_TRUE(docShell,);
     PRUint32 loadType;
     docShell->GetLoadType(&loadType);
     if (loadType == LOAD_RELOAD_NORMAL ||
         loadType == LOAD_RELOAD_BYPASS_CACHE ||
         loadType == LOAD_RELOAD_BYPASS_PROXY ||
         loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE) {
       aEventType = nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD;
     }
   }
       
   nsCOMPtr<nsIDOMDocument> domDoc;
   domWindow->GetDocument(getter_AddRefs(domDoc));
   nsCOMPtr<nsIDOMNode> docNode = do_QueryInterface(domDoc);
-  NS_ENSURE_STATE(docNode);
+  NS_ENSURE_TRUE(docNode,);
 
   nsCOMPtr<nsIAccessible> accessible;
   GetAccessibleFor(docNode, getter_AddRefs(accessible));
   nsRefPtr<nsDocAccessible> docAcc =
     nsAccUtils::QueryAccessibleDocument(accessible);
-  NS_ENSURE_STATE(docAcc);
+  NS_ENSURE_TRUE(docAcc,);
 
   docAcc->FireDocLoadEvents(aEventType);
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent *aTarget)
 {
   nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(aTarget));
 
   nsCOMPtr<nsIAccessible> targetAcc;
@@ -296,40 +279,16 @@ nsAccessibilityService::NotifyOfAnchorJu
 NS_IMETHODIMP
 nsAccessibilityService::FireAccessibleEvent(PRUint32 aEvent,
                                             nsIAccessible *aTarget)
 {
   nsEventShell::FireEvent(aEvent, aTarget);
   return NS_OK;
 }
 
-void nsAccessibilityService::StartLoadCallback(nsITimer *aTimer, void *aClosure)
-{
-  if (gAccessibilityService)
-    gAccessibilityService->
-      ProcessDocLoadEvent(aTimer, aClosure,
-                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_START);
-}
-
-void nsAccessibilityService::EndLoadCallback(nsITimer *aTimer, void *aClosure)
-{
-  if (gAccessibilityService)
-    gAccessibilityService->
-      ProcessDocLoadEvent(aTimer, aClosure,
-                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
-}
-
-void nsAccessibilityService::FailedLoadCallback(nsITimer *aTimer, void *aClosure)
-{
-  if (gAccessibilityService)
-    gAccessibilityService->
-      ProcessDocLoadEvent(aTimer, aClosure,
-                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED);
-}
-
 /* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */
 NS_IMETHODIMP nsAccessibilityService::OnProgressChange(nsIWebProgress *aWebProgress,
   nsIRequest *aRequest, PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress,
   PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress)
 {
   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -35,19 +35,21 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __nsAccessibilityService_h__
 #define __nsAccessibilityService_h__
 
 #include "nsIAccessibilityService.h"
+
+#include "nsCoreUtils.h"
+
 #include "nsCOMArray.h"
 #include "nsIObserver.h"
-#include "nsITimer.h"
 #include "nsIWebProgress.h"
 #include "nsIWebProgressListener.h"
 #include "nsWeakReference.h"
 
 class nsIFrame;
 class nsIWeakReference;
 class nsIDOMNode;
 class nsObjectFrame;
@@ -152,20 +154,29 @@ private:
    * A universal ARIA property is one that can be defined on any element even if there is no role.
    *
    * @param aContent The content node to test
    * @param aWeakShell  A weak reference to the pres shell
    * @return PR_TRUE if there is a universal ARIA property set on the node
    */
   PRBool HasUniversalAriaProperty(nsIContent *aContent, nsIWeakReference *aWeakShell);
 
-  static void StartLoadCallback(nsITimer *aTimer, void *aClosure);
-  static void EndLoadCallback(nsITimer *aTimer, void *aClosure);
-  static void FailedLoadCallback(nsITimer *aTimer, void *aClosure);
-  nsCOMArray<nsITimer> mLoadTimers;
+  /**
+   *  Process the internal doc load event.
+   *
+   *  @param  aWebProgress  [in] the nsIWebProgress object for the load event
+   *  @param  aEventType    [in] the type of load event, one of:
+   *                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_START,
+   *                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE,
+   *                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED
+   */
+  void ProcessDocLoadEvent(nsIWebProgress *aWebProgress, PRUint32 aEventType);
+
+  NS_DECL_RUNNABLEMETHOD_ARG2(nsAccessibilityService, ProcessDocLoadEvent,
+                              nsCOMPtr<nsIWebProgress>, PRUint32)
 };
 
 /**
  * Map nsIAccessibleRole constants to strings. Used by
  * nsIAccessibleRetrieval::getStringRole() method.
  */
 static const char kRoleNames[][20] = {
   "nothing",             //ROLE_NOTHING
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -79,17 +79,16 @@
 #include "nsXPIDLString.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "prdtoa.h"
 #include "nsIAtom.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIURI.h"
-#include "nsITimer.h"
 #include "nsArrayUtils.h"
 #include "nsIMutableArray.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsAttrName.h"
 #include "nsNetUtil.h"
 
@@ -2490,51 +2489,24 @@ NS_IMETHODIMP nsAccessible::ExtendSelect
 NS_IMETHODIMP nsAccessible::GetNativeInterface(void **aOutAccessible)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 nsAccessible::DoCommand(nsIContent *aContent, PRUint32 aActionIndex)
 {
-  if (gDoCommandTimer) {
-    // Already have timer going for another command
-    NS_WARNING("Doubling up on do command timers doesn't work. This wasn't expected.");
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
-  NS_ENSURE_TRUE(timer, NS_ERROR_OUT_OF_MEMORY);
-
   nsCOMPtr<nsIContent> content = aContent;
   if (!content)
     content = do_QueryInterface(mDOMNode);
 
-  // Command closure object memory will be free in DoCommandCallback().
-  nsCommandClosure *closure =
-    new nsCommandClosure(this, content, aActionIndex);
-  NS_ENSURE_TRUE(closure, NS_ERROR_OUT_OF_MEMORY);
-
-  NS_ADDREF(gDoCommandTimer = timer);
-  return gDoCommandTimer->InitWithFuncCallback(DoCommandCallback,
-                                               static_cast<void*>(closure),
-                                               0, nsITimer::TYPE_ONE_SHOT);
-}
-
-void
-nsAccessible::DoCommandCallback(nsITimer *aTimer, void *aClosure)
-{
-  NS_ASSERTION(gDoCommandTimer,
-               "How did we get here if there was no gDoCommandTimer?");
-  NS_RELEASE(gDoCommandTimer);
-
-  nsCommandClosure *closure = static_cast<nsCommandClosure*>(aClosure);
-  closure->accessible->DispatchClickEvent(closure->content,
-                                          closure->actionIndex);
-  delete closure;
+  NS_DISPATCH_RUNNABLEMETHOD_ARG2(DispatchClickEvent, this,
+                                  content, aActionIndex)
+
+  return NS_OK;
 }
 
 void
 nsAccessible::DispatchClickEvent(nsIContent *aContent, PRUint32 aActionIndex)
 {
   if (IsDefunct())
     return;
 
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -370,56 +370,37 @@ protected:
 
   // Hyperlink helpers
   virtual nsresult GetLinkOffset(PRInt32* aStartOffset, PRInt32* aEndOffset);
 
   //////////////////////////////////////////////////////////////////////////////
   // Action helpers
 
   /**
-   * Used to describe click action target. See DoCommand() method.
-   */
-  struct nsCommandClosure
-  {
-    nsCommandClosure(nsAccessible *aAccessible, nsIContent *aContent,
-                     PRUint32 aActionIndex) :
-      accessible(aAccessible), content(aContent), actionIndex(aActionIndex) {}
-
-    nsRefPtr<nsAccessible> accessible;
-    nsCOMPtr<nsIContent> content;
-    PRUint32 actionIndex;
-  };
-
-  /**
    * Prepares click action that will be invoked in timeout.
    *
    * @note  DoCommand() prepares an action in timeout because when action
    *  command opens a modal dialog/window, it won't return until the
    *  dialog/window is closed. If executing action command directly in
    *  nsIAccessible::DoAction() method, it will block AT tools (e.g. GOK) that
    *  invoke action of mozilla accessibles direclty (see bug 277888 for details).
    *
    * @param  aContent      [in, optional] element to click
    * @param  aActionIndex  [in, optional] index of accessible action
    */
   nsresult DoCommand(nsIContent *aContent = nsnull, PRUint32 aActionIndex = 0);
 
   /**
-   * Dispatch click event to target by calling DispatchClickEvent() method.
-   *
-   * @param  aTimer    [in] timer object
-   * @param  aClosure  [in] nsCommandClosure object describing a target.
-   */
-  static void DoCommandCallback(nsITimer *aTimer, void *aClosure);
-
-  /**
    * Dispatch click event.
    */
   virtual void DispatchClickEvent(nsIContent *aContent, PRUint32 aActionIndex);
 
+  NS_DECL_RUNNABLEMETHOD_ARG2(nsAccessible, DispatchClickEvent,
+                              nsCOMPtr<nsIContent>, PRUint32)
+
   //////////////////////////////////////////////////////////////////////////////
   // Helpers
 
   // Check the visibility across both parent content and chrome
   PRBool CheckVisibilityInParentChain(nsIDocument* aDocument, nsIView* aView);
 
   /**
    *  Get the container node for an atomic region, defined by aria-atomic="true"
--- a/accessible/src/base/nsCoreUtils.h
+++ b/accessible/src/base/nsCoreUtils.h
@@ -459,10 +459,137 @@ public:
    *
    * @param aNode    [in] DOM node containing the menupopup element as a child
    * @param aIsAnon  [in] specifies whether popup should be searched inside of
    *                  anonymous or explicit content
    */
   static void GeneratePopupTree(nsIDOMNode *aNode, PRBool aIsAnon = PR_FALSE);
 };
 
+////////////////////////////////////////////////////////////////////////////////
+// nsRunnable helpers
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Use NS_DECL_RUNNABLEMETHOD_ macros to declare a runnable class for the given
+ * method of the given class. There are three macros:
+ *  NS_DECL_RUNNABLEMETHOD(Class, Method)
+ *  NS_DECL_RUNNABLEMETHOD_ARG1(Class, Method, Arg1Type)
+ *  NS_DECL_RUNNABLEMETHOD_ARG2(Class, Method, Arg1Type, Arg2Type)
+ * Note Arg1Type and Arg2Type must be types which keeps the objects alive.
+ *
+ * Use NS_DISPATCH_RUNNABLEMETHOD_ macros to create an instance of declared
+ * runnable class and dispatch it to main thread. Availabe macros are:
+ *  NS_DISPATCH_RUNNABLEMETHOD(Method, Object)
+ *  NS_DISPATCH_RUNNABLEMETHOD_ARG1(Method, Object, Arg1)
+ *  NS_DISPATCH_RUNNABLEMETHOD_ARG2(Method, Object, Arg1, Arg2)
+ */
+
+#define NS_DECL_RUNNABLEMETHOD_HELPER(ClassType, Method)                       \
+  void Revoke()                                                                \
+  {                                                                            \
+    NS_IF_RELEASE(mObj);                                                       \
+  }                                                                            \
+                                                                               \
+protected:                                                                     \
+  virtual ~nsRunnableMethod_##Method()                                         \
+  {                                                                            \
+    NS_IF_RELEASE(mObj);                                                       \
+  }                                                                            \
+                                                                               \
+private:                                                                       \
+  ClassType *mObj;                                                             \
+
+
+#define NS_DECL_RUNNABLEMETHOD(ClassType, Method)                              \
+class nsRunnableMethod_##Method : public nsRunnable                            \
+{                                                                              \
+public:                                                                        \
+  nsRunnableMethod_##Method(ClassType *aObj) : mObj(aObj)                      \
+  {                                                                            \
+    NS_IF_ADDREF(mObj);                                                        \
+  }                                                                            \
+                                                                               \
+  NS_IMETHODIMP Run()                                                          \
+  {                                                                            \
+    if (!mObj)                                                                 \
+      return NS_OK;                                                            \
+    (mObj-> Method)();                                                         \
+    return NS_OK;                                                              \
+  }                                                                            \
+                                                                               \
+  NS_DECL_RUNNABLEMETHOD_HELPER(ClassType, Method)                             \
+                                                                               \
+};
+
+#define NS_DECL_RUNNABLEMETHOD_ARG1(ClassType, Method, Arg1Type)               \
+class nsRunnableMethod_##Method : public nsRunnable                            \
+{                                                                              \
+public:                                                                        \
+  nsRunnableMethod_##Method(ClassType *aObj, Arg1Type aArg1) :                 \
+    mObj(aObj), mArg1(aArg1)                                                   \
+  {                                                                            \
+    NS_IF_ADDREF(mObj);                                                        \
+  }                                                                            \
+                                                                               \
+  NS_IMETHODIMP Run()                                                          \
+  {                                                                            \
+    if (!mObj)                                                                 \
+      return NS_OK;                                                            \
+    (mObj-> Method)(mArg1);                                                    \
+    return NS_OK;                                                              \
+  }                                                                            \
+                                                                               \
+  NS_DECL_RUNNABLEMETHOD_HELPER(ClassType, Method)                             \
+  Arg1Type mArg1;                                                              \
+};
+
+#define NS_DECL_RUNNABLEMETHOD_ARG2(ClassType, Method, Arg1Type, Arg2Type)     \
+class nsRunnableMethod_##Method : public nsRunnable                            \
+{                                                                              \
+public:                                                                        \
+                                                                               \
+  nsRunnableMethod_##Method(ClassType *aObj,                                   \
+                            Arg1Type aArg1, Arg2Type aArg2) :                  \
+    mObj(aObj), mArg1(aArg1), mArg2(aArg2)                                     \
+  {                                                                            \
+    NS_IF_ADDREF(mObj);                                                        \
+  }                                                                            \
+                                                                               \
+  NS_IMETHODIMP Run()                                                          \
+  {                                                                            \
+    if (!mObj)                                                                 \
+      return NS_OK;                                                            \
+    (mObj-> Method)(mArg1, mArg2);                                             \
+    return NS_OK;                                                              \
+  }                                                                            \
+                                                                               \
+  NS_DECL_RUNNABLEMETHOD_HELPER(ClassType, Method)                             \
+  Arg1Type mArg1;                                                              \
+  Arg2Type mArg2;                                                              \
+};
+
+#define NS_DISPATCH_RUNNABLEMETHOD(Method, Obj)                                \
+{                                                                              \
+  nsCOMPtr<nsIRunnable> runnable =                                             \
+    new nsRunnableMethod_##Method(Obj);                                        \
+  if (runnable)                                                                \
+    NS_DispatchToMainThread(runnable);                                         \
+}
+
+#define NS_DISPATCH_RUNNABLEMETHOD_ARG1(Method, Obj, Arg1)                     \
+{                                                                              \
+  nsCOMPtr<nsIRunnable> runnable =                                             \
+    new nsRunnableMethod_##Method(Obj, Arg1);                                  \
+  if (runnable)                                                                \
+    NS_DispatchToMainThread(runnable);                                         \
+}
+
+#define NS_DISPATCH_RUNNABLEMETHOD_ARG2(Method, Obj, Arg1, Arg2)               \
+{                                                                              \
+  nsCOMPtr<nsIRunnable> runnable =                                             \
+    new nsRunnableMethod_##Method(Obj, Arg1, Arg2);                            \
+  if (runnable)                                                                \
+    NS_DispatchToMainThread(runnable);                                         \
+}
+
 #endif
 
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -537,18 +537,22 @@ nsRootAccessible::FireAccessibleFocusEve
 
   FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
                              finalFocusNode, nsAccEvent::eRemoveDupes,
                              aIsAsynch, aIsFromUserInput);
 
   return PR_TRUE;
 }
 
-void nsRootAccessible::FireCurrentFocusEvent()
+void
+nsRootAccessible::FireCurrentFocusEvent()
 {
+  if (IsDefunct())
+    return;
+
   nsCOMPtr<nsIDOMNode> focusedNode = GetCurrentFocus();
   if (!focusedNode) {
     return; // No current focus
   }
 
   // Simulate a focus event so that we can reuse code that fires focus for container children like treeitems
   nsCOMPtr<nsIDOMDocumentEvent> docEvent = do_QueryInterface(mDocument);
   if (docEvent) {
@@ -767,23 +771,17 @@ nsresult nsRootAccessible::HandleEventWi
   }
   else
 #endif
   if (eventType.EqualsLiteral("focus")) {
     if (aTargetNode == mDOMNode && mDOMNode != gLastFocusedNode) {
       // Got focus event for the window, we will make sure that an accessible
       // focus event for initial focus is fired. We do this on a short timer
       // because the initial focus may not have been set yet.
-      if (!mFireFocusTimer) {
-        mFireFocusTimer = do_CreateInstance("@mozilla.org/timer;1");
-      }
-      if (mFireFocusTimer) {
-        mFireFocusTimer->InitWithFuncCallback(FireFocusCallback, this,
-                                              0, nsITimer::TYPE_ONE_SHOT);
-      }
+      NS_DISPATCH_RUNNABLEMETHOD(FireCurrentFocusEvent, this)
     }
 
     // Keep a reference to the target node. We might want to change
     // it to the individual radio button or selected item, and send
     // the focus event to that.
     nsCOMPtr<nsIDOMNode> focusedItem(aTargetNode);
 
     if (!treeItemAccessible) {
@@ -929,23 +927,16 @@ void nsRootAccessible::GetTargetNode(nsI
                                                         aTargetNode);
     if (NS_SUCCEEDED(rv) && *aTargetNode)
       return;
   }
 
   NS_ADDREF(*aTargetNode = eventTarget);
 }
 
-void nsRootAccessible::FireFocusCallback(nsITimer *aTimer, void *aClosure)
-{
-  nsRootAccessible *rootAccessible = static_cast<nsRootAccessible*>(aClosure);
-  NS_ASSERTION(rootAccessible, "How did we get here without a root accessible?");
-  rootAccessible->FireCurrentFocusEvent();
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode
 
 nsresult
 nsRootAccessible::Init()
 {
   nsRefPtr<nsApplicationAccessibleWrap> root = GetApplicationAccessible();
   NS_ENSURE_STATE(root);
@@ -965,21 +956,16 @@ nsRootAccessible::Shutdown()
 
   nsRefPtr<nsApplicationAccessibleWrap> root = GetApplicationAccessible();
   NS_ENSURE_STATE(root);
 
   root->RemoveRootAccessible(this);
 
   mCurrentARIAMenubar = nsnull;
 
-  if (mFireFocusTimer) {
-    mFireFocusTimer->Cancel();
-    mFireFocusTimer = nsnull;
-  }
-
   return nsDocAccessibleWrap::Shutdown();
 }
 
 // nsRootAccessible protected member
 already_AddRefed<nsIDocShellTreeItem>
 nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
 {
   if (!aStart) {
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -46,17 +46,16 @@
 #include "nsXULTreeAccessible.h"
 #endif
 
 #include "nsHashtable.h"
 #include "nsCaretAccessible.h"
 #include "nsIDocument.h"
 #include "nsIDOMFocusListener.h"
 #include "nsIDOMFormListener.h"
-#include "nsITimer.h"
 
 #define NS_ROOTACCESSIBLE_IMPL_CID                      \
 {  /* eaba2cf0-21b1-4e2b-b711-d3a89dcd5e1a */           \
   0xeaba2cf0,                                           \
   0x21b1,                                               \
   0x4e2b,                                               \
   { 0xb7, 0x11, 0xd3, 0xa8, 0x9d, 0xcd, 0x5e, 0x1a }    \
 }
@@ -117,21 +116,19 @@ public:
     /**
       * Fire an accessible focus event for the current focused node,
       * if there is a focus.
       */
     void FireCurrentFocusEvent();
 
     nsCaretAccessible *GetCaretAccessible();
 
-  private:
-    nsCOMPtr<nsITimer> mFireFocusTimer;
-    static void FireFocusCallback(nsITimer *aTimer, void *aClosure);
-    
-  protected:
+protected:
+  NS_DECL_RUNNABLEMETHOD(nsRootAccessible, FireCurrentFocusEvent)
+
     nsresult AddEventListeners();
     nsresult RemoveEventListeners();
     nsresult HandleEventWithTarget(nsIDOMEvent* aEvent,
                                    nsIDOMNode* aTargetNode);
     static void GetTargetNode(nsIDOMEvent *aEvent, nsIDOMNode **aTargetNode);
     void TryFireEarlyLoadEvent(nsIDOMNode *aDocNode);
     void GetChromeEventHandler(nsIDOMEventTarget **aChromeTarget);