Bug 378468 - correct handling of show/hiden/reorder events, r=aaronlev
authorsurkov.alexander@gmail.com
Fri, 29 Jun 2007 19:49:32 -0700
changeset 2950 7898510fae66f3759153cd85b73fcd892e128668
parent 2949 60e2d833ba32e68a6f9d198a9f4fa1c167fd7366
child 2951 a85618590924145dd03855d60cf46ee4ece476e5
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaaronlev
bugs378468
milestone1.9a6pre
Bug 378468 - correct handling of show/hiden/reorder events, r=aaronlev
accessible/src/atk/nsAccessibleWrap.cpp
accessible/src/atk/nsAccessibleWrap.h
accessible/src/base/nsAccessibleEventData.h
accessible/src/base/nsDocAccessible.cpp
accessible/src/msaa/nsAccessibleWrap.cpp
accessible/src/msaa/nsAccessibleWrap.h
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -1271,50 +1271,22 @@ nsAccessibleWrap::FireAccessibleEvent(ns
         MAI_LOG_DEBUG(("\n\nReceived: EVENT_HYPERTEXT_LINK_SELECTED\n"));
         atk_focus_tracker_notify(atkObj);
         g_signal_emit_by_name(atkObj,
                               "link_selected",
                               // Selected link index 
                               *(gint *)eventData);
         break;
 
-        // Is a superclass of ATK event children_changed
-    case nsIAccessibleEvent::EVENT_REORDER:
-        AtkChildrenChange *pAtkChildrenChange;
-
-        MAI_LOG_DEBUG(("\n\nReceived: EVENT_REORDER(children_change)\n"));
+    case nsIAccessibleEvent::EVENT_SHOW:
+        return FireAtkShowHideEvent(aEvent, atkObj, PR_TRUE);
 
-        pAtkChildrenChange = NS_REINTERPRET_CAST(AtkChildrenChange *,
-                                                 eventData);
-        nsAccessibleWrap *childAccWrap;
-        if (pAtkChildrenChange && pAtkChildrenChange->child) {
-            childAccWrap = NS_STATIC_CAST(nsAccessibleWrap *,
-                                          pAtkChildrenChange->child);
-            g_signal_emit_by_name (atkObj,
-                                   pAtkChildrenChange->add ? \
-                                   "children_changed::add" : \
-                                   "children_changed::remove",
-                                   pAtkChildrenChange->index,
-                                   childAccWrap->GetAtkObject(),
-                                   NULL);
-        }
-        else {
-            //
-            // EVENT_REORDER is normally fired by "HTML Document".
-            //
-            // In GOK, [only] "children_changed::add" can cause foreground
-            // window accessible to update it children, which will
-            // refresh "UI-Grab" window.
-            //
-            g_signal_emit_by_name (atkObj,
-                                   "children_changed::add",
-                                   -1, NULL, NULL);
-        }
+    case nsIAccessibleEvent::EVENT_HIDE:
+        return FireAtkShowHideEvent(aEvent, atkObj, PR_FALSE);
 
-        break;
         /*
          * Because dealing with menu is very different between nsIAccessible
          * and ATK, and the menu activity is important, specially transfer the
          * following two event.
          * Need more verification by AT test.
          */
     case nsIAccessibleEvent::EVENT_MENU_START:
         MAI_LOG_DEBUG(("\n\nReceived: EVENT_MENU_START\n"));
@@ -1363,31 +1335,27 @@ nsAccessibleWrap::FireAccessibleEvent(ns
       } break;
     case nsIAccessibleEvent::EVENT_DOCUMENT_ATTRIBUTES_CHANGED:
       {
         MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_ATTRIBUTES_CHANGED\n"));
         g_signal_emit_by_name (atkObj, "attributes_changed");
       } break;
 
     case nsIAccessibleEvent::EVENT_MENUPOPUP_START:
-        // fire extra focus event, then go down to EVENT_SHOW
-        atk_focus_tracker_notify(atkObj);
-
-    case nsIAccessibleEvent::EVENT_SHOW:
-        MAI_LOG_DEBUG(("\n\nReceived: EVENT_SHOW\n"));
+        MAI_LOG_DEBUG(("\n\nReceived: EVENT_MENUPOPUP_START\n"));
+        atk_focus_tracker_notify(atkObj); // fire extra focus event
         atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, PR_TRUE);
         atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, PR_TRUE);
         break;
 
-    case nsIAccessibleEvent::EVENT_HIDE:
     case nsIAccessibleEvent::EVENT_MENUPOPUP_END:
-        MAI_LOG_DEBUG(("\n\nReceived: EVENT_HIDE\n"));
+        MAI_LOG_DEBUG(("\n\nReceived: EVENT_MENUPOPUP_END\n"));
         atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, PR_FALSE);
         atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, PR_FALSE);
-        break; 
+        break;
     }
 
     return NS_OK;
 }
 
 nsresult
 nsAccessibleWrap::FireAtkStateChangeEvent(nsIAccessibleEvent *aEvent,
                                           AtkObject *aObject)
@@ -1523,8 +1491,42 @@ nsAccessibleWrap::FireAtkPropChangedEven
     char *signal_name = g_strconcat("property_change::",
                                     values.property_name, NULL);
     g_signal_emit_by_name(aObject, signal_name, &values, NULL);
     g_free (signal_name);
 
     return NS_OK;
 }
 
+nsresult
+nsAccessibleWrap::FireAtkShowHideEvent(nsIAccessibleEvent *aEvent,
+                                       AtkObject *aObject, PRBool aIsAdded)
+{
+    if (aIsAdded)
+        MAI_LOG_DEBUG(("\n\nReceived: EVENT_SHOW\n"));
+    else
+        MAI_LOG_DEBUG(("\n\nReceived: EVENT_HIDE\n"));
+
+    nsCOMPtr<nsIAccessible> accessible;
+    aEvent->GetAccessible(getter_AddRefs(accessible));
+    NS_ENSURE_STATE(accessible);
+
+    nsCOMPtr<nsIAccessible> parentAccessible;
+    accessible->GetParent(getter_AddRefs(parentAccessible));
+    NS_ENSURE_STATE(parentAccessible);
+
+    PRInt32 indexInParent = -1;
+    accessible->GetIndexInParent(&indexInParent);
+
+    AtkObject *parentObject = GetAtkObject(parentAccessible);
+    NS_ENSURE_STATE(parentObject);
+
+    g_signal_emit_by_name(parentObject,
+                          aIsAdded ? \
+                          "children_changed::add" : \
+                          "children_changed::remove",
+                          indexInParent,
+                          aObject,
+                          NULL);
+
+    return NS_OK;
+}
+
--- a/accessible/src/atk/nsAccessibleWrap.h
+++ b/accessible/src/atk/nsAccessibleWrap.h
@@ -116,16 +116,18 @@ public:
 
 protected:
     nsresult FireAtkStateChangeEvent(nsIAccessibleEvent *aEvent,
                                      AtkObject *aObject);
     nsresult FireAtkTextChangedEvent(nsIAccessibleEvent *aEvent,
                                      AtkObject *aObject);
     nsresult FireAtkPropChangedEvent(nsIAccessibleEvent *aEvent,
                                      AtkObject *aObject);
+    nsresult FireAtkShowHideEvent(nsIAccessibleEvent *aEvent,
+                                  AtkObject *aObject, PRBool aIsAdded);
 
     AtkObject *mAtkObject;
 
 private:
     PRUint16 CreateMaiInterfaces(void);
 };
 
 #endif /* __NS_ACCESSIBLE_WRAP_H__ */
--- a/accessible/src/base/nsAccessibleEventData.h
+++ b/accessible/src/base/nsAccessibleEventData.h
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 1998
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Kyle Yuan (kyle.yuan@sun.com)
  *   John Sun (john.sun@sun.com)
+ *   Alexander Surkov <surkov.alexander@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -123,23 +124,18 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_NSIACCESSIBLEEVENT(nsAccEvent::)
   NS_DECL_NSIACCESSIBLECARETMOVEEVENT
 
 private:
   PRInt32 mCaretOffset;
 };
 
-// XXX todo: We might want to use XPCOM interfaces instead of structs
-//     e.g., nsAccessibleTextChangeEvent: public nsIAccessibleTextChangeEvent
-
-struct AtkChildrenChange {
-  PRInt32      index;  // index of child in parent 
-  nsIAccessible *child;   
-  PRBool        add;    // true for add, false for delete
-};
+// XXX todo: We might want to use XPCOM interfaces instead of struct
+//     e.g., nsAccessibleTableChangeEvent: public nsIAccessibleTableChangeEvent
 
 struct AtkTableChange {
   PRUint32 index;   // the start row/column after which the rows are inserted/deleted.
   PRUint32 count;   // the number of inserted/deleted rows/columns
 };
 
-#endif  
+#endif
+
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1289,49 +1289,49 @@ void nsDocAccessible::FlushEventsCallbac
 {
   nsPIAccessibleDocument *accessibleDoc = NS_STATIC_CAST(nsPIAccessibleDocument*, aClosure);
   NS_ASSERTION(accessibleDoc, "How did we get here without an accessible document?");
   accessibleDoc->FlushPendingEvents();
 }
 
 void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent)
 {
+  NS_ASSERTION(aChangeEvent != nsIAccessibleEvent::EVENT_SHOW,
+               "nsDocAccessible::RefreshNodes isn't supposed to work with show event.");
+
   nsCOMPtr<nsIDOMNode> iterNode(aStartNode), nextNode;
   nsCOMPtr<nsIAccessNode> accessNode;
 
   do {
     GetCachedAccessNode(iterNode, getter_AddRefs(accessNode));
     if (accessNode) {
       // Accessibles that implement their own subtrees,
       // like html combo boxes and xul trees must shutdown all of their own
       // children when they override Shutdown()
 
       // Don't shutdown our doc object!
       if (accessNode != NS_STATIC_CAST(nsIAccessNode*, this)) {
-        if (aChangeEvent != nsIAccessibleEvent::EVENT_SHOW) {
-          nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
-          if (accessible) {
-            // Fire menupopupend events for menu popups that go away
-            PRUint32 role, event = 0;
-            accessible->GetFinalRole(&role);
-            if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
-              nsCOMPtr<nsIDOMNode> domNode;
-              accessNode->GetDOMNode(getter_AddRefs(domNode));
-              nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(domNode));
-              if (!popup) {
-                // Popup elements already fire these via DOMMenuInactive
-                // handling in nsRootAccessible::HandleEvent
-                event = nsIAccessibleEvent::EVENT_MENUPOPUP_END;
-              }
-            }
-            if (event) {
-              FireToolkitEvent(event, accessible, nsnull);
+
+        nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
+        if (accessible) {
+          // Fire menupopupend events for menu popups that go away
+          PRUint32 role = Role(accessible);
+          if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
+            nsCOMPtr<nsIDOMNode> domNode;
+            accessNode->GetDOMNode(getter_AddRefs(domNode));
+            nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(domNode));
+            if (!popup) {
+              // Popup elements already fire these via DOMMenuInactive
+              // handling in nsRootAccessible::HandleEvent
+              FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
+                               accessible, nsnull);
             }
           }
         }
+
         void *uniqueID;
         accessNode->GetUniqueID(&uniqueID);
         nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
         privateAccessNode->Shutdown();
         // Remove from hash table as well
         mAccessNodeCache.Remove(uniqueID);
       }
     }
@@ -1398,38 +1398,39 @@ NS_IMETHODIMP nsDocAccessible::Invalidat
   nsCOMPtr<nsIAccessible> childAccessible = do_QueryInterface(childAccessNode);
   if (!childAccessible && aChangeEventType != nsIAccessibleEvent::EVENT_HIDE) {
     // If not about to hide it, make sure there's an accessible so we can fire an
     // event for it
     GetAccService()->GetAccessibleFor(childNode, getter_AddRefs(childAccessible));
   }
   nsCOMPtr<nsPIAccessible> privateChildAccessible =
     do_QueryInterface(childAccessible);
+  NS_ENSURE_STATE(privateChildAccessible);
+
 #ifdef DEBUG_A11Y
   nsAutoString localName;
   childNode->GetLocalName(localName);
   const char *hasAccessible = childAccessible ? " (acc)" : "";
   if (aChangeEventType == nsIAccessibleEvent::EVENT_HIDE) {
     printf("[Hide %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
   }
   else if (aChangeEventType == nsIAccessibleEvent::EVENT_SHOW) {
     printf("[Show %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
   }
   else if (aChangeEventType == nsIAccessibleEvent::EVENT_REORDER) {
     printf("[Reorder %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
   }
 #endif
 
-  if (aChangeEventType == nsIAccessibleEvent::EVENT_HIDE) {
-    // Fire EVENT_HIDE or EVENT_MENUPOPUP_END if previous accessible existed
-    // for node being hidden. Fire this before the accessible goes away
-    if (privateChildAccessible) {
-      privateChildAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_HIDE,
-                                               childAccessible, nsnull);
-    }
+  if (aChangeEventType == nsIAccessibleEvent::EVENT_HIDE ||
+      aChangeEventType == nsIAccessibleEvent::EVENT_REORDER) {
+    // Fire EVENT_HIDE if previous accessible existed for node being hidden.
+    // Fire this before the accessible goes away.
+    privateChildAccessible->FireToolkitEvent(nsIAccessibleEvent::EVENT_HIDE,
+                                             childAccessible, nsnull);
   }
 
   // Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in this subtree
   if (aChangeEventType != nsIAccessibleEvent::EVENT_SHOW) {
     RefreshNodes(childNode, aChangeEventType);
   }
 
   // We need to get an accessible for the mutation event's container node
@@ -1460,17 +1461,18 @@ NS_IMETHODIMP nsDocAccessible::Invalidat
     nsCOMPtr<nsIDOMNode> containerNode;
     containerAccessNode->GetDOMNode(getter_AddRefs(containerNode));
     if (containerNode) {
       FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_REORDER,
                               containerNode, nsnull);
     }
   }
 
-  if (aChangeEventType == nsIAccessibleEvent::EVENT_SHOW && aChild) {
+  if (aChild && (aChangeEventType == nsIAccessibleEvent::EVENT_SHOW ||
+      aChangeEventType == nsIAccessibleEvent::EVENT_REORDER)) {
     // Fire EVENT_SHOW, EVENT_MENUPOPUP_START for newly visible content.
     // Fire after a short timer, because we want to make sure the view has been
     // updated to make this accessible content visible. If we don't wait,
     // the assistive technology may receive the event and then retrieve
     // nsIAccessibleStates::STATE_INVISIBLE for the event's accessible object.
     FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_SHOW, childNode, nsnull);
     nsAutoString role;
     if (GetRoleAttribute(aChild, role) &&
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -1524,52 +1524,19 @@ nsAccessibleWrap::FireAccessibleEvent(ns
   nsCOMPtr<nsIAccessible> newAccessible;
   if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
     // Don't use frame from current accessible when we're hiding that
     // accessible.
     accessible->GetParent(getter_AddRefs(newAccessible));
   } else {
     newAccessible = accessible;
   }
-  
-  HWND hWnd = 0;
-  nsCOMPtr<nsPIAccessNode> privateAccessNode =
-    do_QueryInterface(newAccessible);
-  if (privateAccessNode) {
-    nsIFrame *frame = privateAccessNode->GetFrame();
-    if (frame) {
-      nsIWidget *window = frame->GetWindow();
-      PRBool isVisible;
-      window->IsVisible(isVisible);
-      if (isVisible) {
-        // Short explanation:
-        // If HWND for frame is inside a hidden window, fire the event on the 
-        // containing document's visible window.
-        //
-        // Long explanation:
-        // This is really just to fix combo boxes with JAWS. Window-Eyes already worked with
-        // combo boxes because they use the value change event in the closed combo box
-        // case. JAWS will only pay attention to the focus events on the list items.
-        // The JAWS developers haven't fixed that, so we'll use the focus events to make JAWS work.
-        // However, JAWS is ignoring events on a hidden window. So, in order to fix the bug where
-        // JAWS doesn't echo the current option as it changes in a closed combo box, we need to use an
-        // ensure that we never fire an event with an HWND for a hidden window.
-        hWnd = (HWND)frame->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
-      }
-    }
-  }
 
-  if (!hWnd) {
-    void* handle = nsnull;
-    nsCOMPtr<nsIAccessibleDocument> accessibleDoc;
-    accessNode->GetAccessibleDocument(getter_AddRefs(accessibleDoc));
-    NS_ENSURE_STATE(accessibleDoc);
-    accessibleDoc->GetWindowHandle(&handle);
-    hWnd = (HWND)handle;
-  }
+  HWND hWnd = GetHWNDFor(accessible);
+  NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
 
   // Gecko uses two windows for every scrollable area. One window contains
   // scrollbars and the child window contains only the client area.
   // Details of the 2 window system:
   // * Scrollbar window: caret drawing window & return value for WindowFromAccessibleObject()
   // * Client area window: text drawing window & MSAA event window
 
   // Fire MSAA event for client area window.
@@ -1593,16 +1560,64 @@ PRInt32 nsAccessibleWrap::GetChildIDFor(
   }
   accessNode->GetUniqueID(&uniqueID);
 
   // Yes, this means we're only compatibible with 32 bit
   // MSAA is only available for 32 bit windows, so it's okay
   return - NS_PTR_TO_INT32(uniqueID);
 }
 
+HWND
+nsAccessibleWrap::GetHWNDFor(nsIAccessible *aAccessible)
+{
+  nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
+  nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
+  if (!privateAccessNode)
+    return 0;
+
+  HWND hWnd = 0;
+
+  nsIFrame *frame = privateAccessNode->GetFrame();
+  if (frame) {
+    nsIWidget *window = frame->GetWindow();
+    PRBool isVisible;
+    window->IsVisible(isVisible);
+    if (isVisible) {
+      // Short explanation:
+      // If HWND for frame is inside a hidden window, fire the event on the
+      // containing document's visible window.
+      //
+      // Long explanation:
+      // This is really just to fix combo boxes with JAWS. Window-Eyes already
+      // worked with combo boxes because they use the value change event in
+      // the closed combo box case. JAWS will only pay attention to the focus
+      // events on the list items. The JAWS developers haven't fixed that, so
+      // we'll use the focus events to make JAWS work. However, JAWS is
+      // ignoring events on a hidden window. So, in order to fix the bug where
+      // JAWS doesn't echo the current option as it changes in a closed
+      // combo box, we need to use an ensure that we never fire an event with
+      // an HWND for a hidden window.
+      hWnd = (HWND)frame->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
+    }
+  }
+
+  if (!hWnd) {
+    void* handle = nsnull;
+    nsCOMPtr<nsIAccessibleDocument> accessibleDoc;
+    accessNode->GetAccessibleDocument(getter_AddRefs(accessibleDoc));
+    if (!accessibleDoc)
+      return 0;
+
+    accessibleDoc->GetWindowHandle(&handle);
+    hWnd = (HWND)handle;
+  }
+
+  return hWnd;
+}
+
 IDispatch *nsAccessibleWrap::NativeAccessible(nsIAccessible *aXPAccessible)
 {
   if (!aXPAccessible) {
    NS_WARNING("Not passing in an aXPAccessible");
    return NULL;
   }
 
   nsCOMPtr<nsIAccessibleWin32Object> accObject(do_QueryInterface(aXPAccessible));
--- a/accessible/src/msaa/nsAccessibleWrap.h
+++ b/accessible/src/msaa/nsAccessibleWrap.h
@@ -290,16 +290,17 @@ class nsAccessibleWrap : public nsAccess
         LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
         VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
 
   // nsPIAccessible
   NS_IMETHOD FireAccessibleEvent(nsIAccessibleEvent *aEvent);
 
   // Helper methods
   static PRInt32 GetChildIDFor(nsIAccessible* aAccessible);
+  static HWND GetHWNDFor(nsIAccessible *aAccessible);
 
   /**
    * System caret support: update the Windows caret position. 
    * The system caret works more universally than the MSAA caret
    * For example, Window-Eyes, JAWS, ZoomText and Windows Tablet Edition use it
    * We will use an invisible system caret.
    * Gecko is still responsible for drawing its own caret
    */