nsresult AccessibleWrap::FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject* aObject) { MAI_LOG_DEBUG(("\n\nReceived: EVENT_STATE_CHANGE\n")); AccStateChangeEvent* event = downcast_accEvent(aEvent); NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); bool isEnabled = event->IsStateEnabled(); PRInt32 stateIndex = AtkStateMap::GetStateIndexFor(event->GetState()); if (stateIndex >= 0) { NS_ASSERTION(gAtkStateMap[stateIndex].stateMapEntryType != kNoSuchState, "No such state"); if (gAtkStateMap[stateIndex].atkState != kNone) { NS_ASSERTION(gAtkStateMap[stateIndex].stateMapEntryType != kNoStateChange, "State changes should not fired for this state"); if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) isEnabled = !isEnabled; // Fire state change for first state if there is one to map atk_object_notify_state_change(aObject, gAtkStateMap[stateIndex].atkState, isEnabled); } } return NS_OK; }
already_AddRefed<nsIAccessibleEvent> a11y::MakeXPCEvent(AccEvent* aEvent) { DocAccessible* doc = aEvent->GetDocAccessible(); Accessible* acc = aEvent->GetAccessible(); nsINode* node = acc->GetNode(); nsIDOMNode* domNode = node ? node->AsDOMNode() : nullptr; bool fromUser = aEvent->IsFromUserInput(); uint32_t type = aEvent->GetEventType(); uint32_t eventGroup = aEvent->GetEventGroups(); nsCOMPtr<nsIAccessibleEvent> xpEvent; if (eventGroup & (1 << AccEvent::eStateChangeEvent)) { AccStateChangeEvent* sc = downcast_accEvent(aEvent); bool extra = false; uint32_t state = nsAccUtils::To32States(sc->GetState(), &extra); xpEvent = new xpcAccStateChangeEvent(type, acc, doc, domNode, fromUser, state, extra, sc->IsStateEnabled()); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eTextChangeEvent)) { AccTextChangeEvent* tc = downcast_accEvent(aEvent); nsString text; tc->GetModifiedText(text); xpEvent = new xpcAccTextChangeEvent(type, acc, doc, domNode, fromUser, tc->GetStartOffset(), tc->GetLength(), tc->IsTextInserted(), text); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eHideEvent)) { AccHideEvent* hideEvent = downcast_accEvent(aEvent); xpEvent = new xpcAccHideEvent(type, acc, doc, domNode, fromUser, hideEvent->TargetParent(), hideEvent->TargetNextSibling(), hideEvent->TargetPrevSibling()); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eCaretMoveEvent)) { AccCaretMoveEvent* cm = downcast_accEvent(aEvent); xpEvent = new xpcAccCaretMoveEvent(type, acc, doc, domNode, fromUser, cm->GetCaretOffset()); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eVirtualCursorChangeEvent)) { AccVCChangeEvent* vcc = downcast_accEvent(aEvent); xpEvent = new xpcAccVirtualCursorChangeEvent(type, acc, doc, domNode, fromUser, vcc->OldAccessible(), vcc->OldStartOffset(), vcc->OldEndOffset(), vcc->Reason()); return xpEvent.forget(); } xpEvent = new xpcAccEvent(type, acc, doc, domNode, fromUser); return xpEvent.forget(); }
nsresult HTMLFileInputAccessible::HandleAccEvent(AccEvent* aEvent) { nsresult rv = HyperTextAccessibleWrap::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); // Redirect state change events for inherited states to child controls. Note, // unavailable state is not redirected. That's a standard for unavailable // state handling. AccStateChangeEvent* event = downcast_accEvent(aEvent); if (event && (event->GetState() == states::BUSY || event->GetState() == states::REQUIRED || event->GetState() == states::HASPOPUP || event->GetState() == states::INVALID)) { Accessible* input = GetChildAt(0); if (input && input->Role() == roles::ENTRY) { nsRefPtr<AccStateChangeEvent> childEvent = new AccStateChangeEvent(input, event->GetState(), event->IsStateEnabled(), (event->IsFromUserInput() ? eFromUserInput : eNoUserInput)); nsEventShell::FireEvent(childEvent); } Accessible* button = GetChildAt(1); if (button && button->Role() == roles::PUSHBUTTON) { nsRefPtr<AccStateChangeEvent> childEvent = new AccStateChangeEvent(button, event->GetState(), event->IsStateEnabled(), (event->IsFromUserInput() ? eFromUserInput : eNoUserInput)); nsEventShell::FireEvent(childEvent); } } return NS_OK; }
static void GetDocLoadEventType(AccEvent* aEvent, nsACString& aEventType) { uint32_t type = aEvent->GetEventType(); if (type == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED) { aEventType.AssignLiteral("load stopped"); } else if (type == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE) { aEventType.AssignLiteral("load complete"); } else if (type == nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD) { aEventType.AssignLiteral("reload"); } else if (type == nsIAccessibleEvent::EVENT_STATE_CHANGE) { AccStateChangeEvent* event = downcast_accEvent(aEvent); if (event->GetState() == states::BUSY) { aEventType.AssignLiteral("busy "); if (event->IsStateEnabled()) aEventType.AppendLiteral("true"); else aEventType.AppendLiteral("false"); } } }
nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { nsresult rv = Accessible::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); Accessible* accessible = aEvent->GetAccessible(); NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE); // The accessible can become defunct if we have an xpcom event listener // which decides it would be fun to change the DOM and flush layout. if (accessible->IsDefunct()) return NS_OK; uint32_t type = aEvent->GetEventType(); AtkObject* atkObj = AccessibleWrap::GetAtkObject(accessible); // We don't create ATK objects for plain text leaves, just return NS_OK in // such case. if (!atkObj) { NS_ASSERTION(type == nsIAccessibleEvent::EVENT_SHOW || type == nsIAccessibleEvent::EVENT_HIDE, "Event other than SHOW and HIDE fired for plain text leaves"); return NS_OK; } AccessibleWrap* accWrap = GetAccessibleWrap(atkObj); if (!accWrap) { return NS_OK; // Node is shut down } switch (type) { case nsIAccessibleEvent::EVENT_STATE_CHANGE: { AccStateChangeEvent* event = downcast_accEvent(aEvent); MAI_ATK_OBJECT(atkObj)->FireStateChangeEvent(event->GetState(), event->IsStateEnabled()); break; } case nsIAccessibleEvent::EVENT_TEXT_REMOVED: case nsIAccessibleEvent::EVENT_TEXT_INSERTED: return FireAtkTextChangedEvent(aEvent, atkObj); case nsIAccessibleEvent::EVENT_FOCUS: { a11y::RootAccessible* rootAccWrap = accWrap->RootAccessible(); if (rootAccWrap && rootAccWrap->mActivated) { atk_focus_tracker_notify(atkObj); // Fire state change event for focus atk_object_notify_state_change(atkObj, ATK_STATE_FOCUSED, true); return NS_OK; } } break; case nsIAccessibleEvent::EVENT_NAME_CHANGE: { nsAutoString newName; accessible->Name(newName); MaybeFireNameChange(atkObj, newName); break; } case nsIAccessibleEvent::EVENT_VALUE_CHANGE: if (accessible->HasNumericValue()) { // Make sure this is a numeric value. Don't fire for string value changes // (e.g. text editing) ATK values are always numeric. g_object_notify((GObject*)atkObj, "accessible-value"); } break; case nsIAccessibleEvent::EVENT_SELECTION: case nsIAccessibleEvent::EVENT_SELECTION_ADD: case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: { // XXX: dupe events may be fired AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent); g_signal_emit_by_name(AccessibleWrap::GetAtkObject(selChangeEvent->Widget()), "selection_changed"); break; } case nsIAccessibleEvent::EVENT_SELECTION_WITHIN: { g_signal_emit_by_name(atkObj, "selection_changed"); break; } case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: g_signal_emit_by_name(atkObj, "text_selection_changed"); break; case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: { AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(aEvent); NS_ASSERTION(caretMoveEvent, "Event needs event data"); if (!caretMoveEvent) break; int32_t caretOffset = caretMoveEvent->GetCaretOffset(); g_signal_emit_by_name(atkObj, "text_caret_moved", caretOffset); } break; case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED: g_signal_emit_by_name(atkObj, "text-attributes-changed"); break; case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED: g_signal_emit_by_name(atkObj, "model_changed"); break; case nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT: { AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); int32_t rowIndex = tableEvent->GetIndex(); int32_t numRows = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "row_inserted", rowIndex, numRows); } break; case nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE: { AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); int32_t rowIndex = tableEvent->GetIndex(); int32_t numRows = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "row_deleted", rowIndex, numRows); } break; case nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER: { g_signal_emit_by_name(atkObj, "row_reordered"); break; } case nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT: { AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); int32_t colIndex = tableEvent->GetIndex(); int32_t numCols = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "column_inserted", colIndex, numCols); } break; case nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE: { AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); int32_t colIndex = tableEvent->GetIndex(); int32_t numCols = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "column_deleted", colIndex, numCols); } break; case nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER: g_signal_emit_by_name(atkObj, "column_reordered"); break; case nsIAccessibleEvent::EVENT_SECTION_CHANGED: g_signal_emit_by_name(atkObj, "visible_data_changed"); break; case nsIAccessibleEvent::EVENT_SHOW: return FireAtkShowHideEvent(aEvent, atkObj, true); case nsIAccessibleEvent::EVENT_HIDE: // XXX - Handle native dialog accessibles. if (!accessible->IsRoot() && accessible->HasARIARole() && accessible->ARIARole() == roles::DIALOG) { guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } return FireAtkShowHideEvent(aEvent, atkObj, false); /* * 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: case nsIAccessibleEvent::EVENT_MENU_END: break; case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE: { accessible->AsRoot()->mActivated = true; guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); // Always fire a current focus event after activation. FocusMgr()->ForceFocusEvent(); } break; case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE: { accessible->AsRoot()->mActivated = false; guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE: { guint id = g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE: { guint id = g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_WINDOW_RESTORE: { guint id = g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE: g_signal_emit_by_name (atkObj, "load_complete"); // XXX - Handle native dialog accessibles. if (!accessible->IsRoot() && accessible->HasARIARole() && accessible->ARIARole() == roles::DIALOG) { guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD: g_signal_emit_by_name (atkObj, "reload"); break; case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED: g_signal_emit_by_name (atkObj, "load_stopped"); break; case nsIAccessibleEvent::EVENT_MENUPOPUP_START: atk_focus_tracker_notify(atkObj); // fire extra focus event atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, true); atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, true); break; case nsIAccessibleEvent::EVENT_MENUPOPUP_END: atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, false); atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, false); break; } return NS_OK; }
nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { auto accessible = static_cast<AccessibleWrap*>(aEvent->GetAccessible()); NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE); DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(accessible->Document()); if (doc) { switch (aEvent->GetEventType()) { case nsIAccessibleEvent::EVENT_FOCUS: { if (DocAccessibleWrap* topContentDoc = doc->GetTopLevelContentDoc(accessible)) { topContentDoc->CacheFocusPath(accessible); } break; } case nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED: { AccVCChangeEvent* vcEvent = downcast_accEvent(aEvent); auto newPosition = static_cast<AccessibleWrap*>(vcEvent->NewAccessible()); if (newPosition) { if (DocAccessibleWrap* topContentDoc = doc->GetTopLevelContentDoc(accessible)) { topContentDoc->CacheFocusPath(newPosition); } } break; } } } nsresult rv = Accessible::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); if (IPCAccessibilityActive()) { return NS_OK; } // The accessible can become defunct if we have an xpcom event listener // which decides it would be fun to change the DOM and flush layout. if (accessible->IsDefunct() || !accessible->IsBoundToParent()) { return NS_OK; } if (doc) { if (!nsCoreUtils::IsContentDocument(doc->DocumentNode())) { return NS_OK; } } SessionAccessibility* sessionAcc = SessionAccessibility::GetInstanceFor(accessible); if (!sessionAcc) { return NS_OK; } switch (aEvent->GetEventType()) { case nsIAccessibleEvent::EVENT_FOCUS: sessionAcc->SendFocusEvent(accessible); break; case nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED: { AccVCChangeEvent* vcEvent = downcast_accEvent(aEvent); auto newPosition = static_cast<AccessibleWrap*>(vcEvent->NewAccessible()); auto oldPosition = static_cast<AccessibleWrap*>(vcEvent->OldAccessible()); if (sessionAcc && newPosition) { if (oldPosition != newPosition) { if (vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT) { sessionAcc->SendHoverEnterEvent(newPosition); } else { sessionAcc->SendAccessibilityFocusedEvent(newPosition); } } if (vcEvent->BoundaryType() != nsIAccessiblePivot::NO_BOUNDARY) { sessionAcc->SendTextTraversedEvent( newPosition, vcEvent->NewStartOffset(), vcEvent->NewEndOffset()); } } break; } case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: { AccCaretMoveEvent* event = downcast_accEvent(aEvent); sessionAcc->SendTextSelectionChangedEvent(accessible, event->GetCaretOffset()); break; } case nsIAccessibleEvent::EVENT_TEXT_INSERTED: case nsIAccessibleEvent::EVENT_TEXT_REMOVED: { AccTextChangeEvent* event = downcast_accEvent(aEvent); sessionAcc->SendTextChangedEvent( accessible, event->ModifiedText(), event->GetStartOffset(), event->GetLength(), event->IsTextInserted(), event->IsFromUserInput()); break; } case nsIAccessibleEvent::EVENT_STATE_CHANGE: { AccStateChangeEvent* event = downcast_accEvent(aEvent); auto state = event->GetState(); if (state & states::CHECKED) { sessionAcc->SendClickedEvent(accessible, event->IsStateEnabled()); } if (state & states::SELECTED) { sessionAcc->SendSelectedEvent(accessible, event->IsStateEnabled()); } if (state & states::BUSY) { sessionAcc->SendWindowStateChangedEvent(accessible); } break; } case nsIAccessibleEvent::EVENT_SCROLLING: { AccScrollingEvent* event = downcast_accEvent(aEvent); sessionAcc->SendScrollingEvent(accessible, event->ScrollX(), event->ScrollY(), event->MaxScrollX(), event->MaxScrollY()); break; } case nsIAccessibleEvent::EVENT_SHOW: case nsIAccessibleEvent::EVENT_HIDE: { AccMutationEvent* event = downcast_accEvent(aEvent); auto parent = static_cast<AccessibleWrap*>(event->Parent()); sessionAcc->SendWindowContentChangedEvent(parent); break; } default: break; } return NS_OK; }