Commit 5a14674c authored by surkov.alexander@gmail.com's avatar surkov.alexander@gmail.com
Browse files

Bug 378468 - correct handling of show/hiden/reorder events, r=aaronlev

parent d92361a7
Loading
Loading
Loading
Loading
+43 −41
Original line number Diff line number Diff line
@@ -1276,40 +1276,12 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
                              *(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"));

        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_SHOW:
        return FireAtkShowHideEvent(aEvent, atkObj, PR_TRUE);

    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
@@ -1368,18 +1340,14 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
      } 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;
@@ -1528,3 +1496,37 @@ nsAccessibleWrap::FireAtkPropChangedEvent(nsIAccessibleEvent *aEvent,
    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;
}
+2 −0
Original line number Diff line number Diff line
@@ -121,6 +121,8 @@ protected:
                                     AtkObject *aObject);
    nsresult FireAtkPropChangedEvent(nsIAccessibleEvent *aEvent,
                                     AtkObject *aObject);
    nsresult FireAtkShowHideEvent(nsIAccessibleEvent *aEvent,
                                  AtkObject *aObject, PRBool aIsAdded);

    AtkObject *mAtkObject;

+5 −9
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
 * 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"),
@@ -128,14 +129,8 @@ 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.
@@ -143,3 +138,4 @@ struct AtkTableChange {
};

#endif
+28 −26
Original line number Diff line number Diff line
@@ -1294,6 +1294,9 @@ void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure)

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;

@@ -1306,12 +1309,11 @@ void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent

      // 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);
          PRUint32 role = Role(accessible);
          if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
            nsCOMPtr<nsIDOMNode> domNode;
            accessNode->GetDOMNode(getter_AddRefs(domNode));
@@ -1319,14 +1321,12 @@ void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent
            if (!popup) {
              // Popup elements already fire these via DOMMenuInactive
              // handling in nsRootAccessible::HandleEvent
                event = nsIAccessibleEvent::EVENT_MENUPOPUP_END;
              }
            }
            if (event) {
              FireToolkitEvent(event, accessible, nsnull);
              FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
                               accessible, nsnull);
            }
          }
        }

        void *uniqueID;
        accessNode->GetUniqueID(&uniqueID);
        nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
@@ -1403,6 +1403,8 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
  }
  nsCOMPtr<nsPIAccessible> privateChildAccessible =
    do_QueryInterface(childAccessible);
  NS_ENSURE_STATE(privateChildAccessible);

#ifdef DEBUG_A11Y
  nsAutoString localName;
  childNode->GetLocalName(localName);
@@ -1418,14 +1420,13 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
  }
#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) {
  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) {
@@ -1465,7 +1466,8 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
    }
  }

  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,
+50 −35
Original line number Diff line number Diff line
@@ -1530,41 +1530,8 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
    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.
@@ -1598,6 +1565,54 @@ PRInt32 nsAccessibleWrap::GetChildIDFor(nsIAccessible* aAccessible)
  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) {
Loading