nsresult AccessibleWrap::FirePlatformEvent(AccEvent* aEvent) { Accessible* accessible = aEvent->GetAccessible(); NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE); PRUint32 type = aEvent->GetEventType(); AtkObject* atkObj = AccessibleWrap::GetAtkObject(accessible); // We don't create ATK objects for nsIAccessible 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: return FireAtkStateChangeEvent(aEvent, atkObj); case nsIAccessibleEvent::EVENT_TEXT_REMOVED: case nsIAccessibleEvent::EVENT_TEXT_INSERTED: return FireAtkTextChangedEvent(aEvent, atkObj); case nsIAccessibleEvent::EVENT_FOCUS: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_FOCUS\n")); a11y::RootAccessible* rootAccWrap = accWrap->RootAccessible(); if (rootAccWrap && rootAccWrap->mActivated) { atk_focus_tracker_notify(atkObj); // Fire state change event for focus nsRefPtr<AccEvent> stateChangeEvent = new AccStateChangeEvent(accessible, states::FOCUSED, true); return FireAtkStateChangeEvent(stateChangeEvent, atkObj); } } break; case nsIAccessibleEvent::EVENT_NAME_CHANGE: { nsAutoString newName; accessible->Name(newName); NS_ConvertUTF16toUTF8 utf8Name(newName); if (!atkObj->name || !utf8Name.Equals(atkObj->name)) atk_object_set_name(atkObj, utf8Name.get()); break; } case nsIAccessibleEvent::EVENT_VALUE_CHANGE: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_VALUE_CHANGE\n")); nsCOMPtr<nsIAccessibleValue> value(do_QueryObject(accessible)); if (value) { // Make sure this is a numeric value // Don't fire for MSAA 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 MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n")); AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent); g_signal_emit_by_name(AccessibleWrap::GetAtkObject(selChangeEvent->Widget()), "selection_changed"); break; } case nsIAccessibleEvent::EVENT_SELECTION_WITHIN: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n")); g_signal_emit_by_name(atkObj, "selection_changed"); break; } case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_SELECTION_CHANGED\n")); g_signal_emit_by_name(atkObj, "text_selection_changed"); break; case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_CARET_MOVED\n")); AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(aEvent); NS_ASSERTION(caretMoveEvent, "Event needs event data"); if (!caretMoveEvent) break; PRInt32 caretOffset = caretMoveEvent->GetCaretOffset(); MAI_LOG_DEBUG(("\n\nCaret postion: %d", caretOffset)); g_signal_emit_by_name(atkObj, "text_caret_moved", // Curent caret position caretOffset); } break; case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED: MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_ATTRIBUTE_CHANGED\n")); g_signal_emit_by_name(atkObj, "text-attributes-changed"); break; case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED: MAI_LOG_DEBUG(("\n\nReceived: EVENT_TABLE_MODEL_CHANGED\n")); g_signal_emit_by_name(atkObj, "model_changed"); break; case nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_TABLE_ROW_INSERT\n")); AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); PRInt32 rowIndex = tableEvent->GetIndex(); PRInt32 numRows = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "row_inserted", // After which the rows are inserted rowIndex, // The number of the inserted numRows); } break; case nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_TABLE_ROW_DELETE\n")); AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); PRInt32 rowIndex = tableEvent->GetIndex(); PRInt32 numRows = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "row_deleted", // After which the rows are deleted rowIndex, // The number of the deleted numRows); } break; case nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_TABLE_ROW_REORDER\n")); g_signal_emit_by_name(atkObj, "row_reordered"); break; } case nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_TABLE_COLUMN_INSERT\n")); AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); PRInt32 colIndex = tableEvent->GetIndex(); PRInt32 numCols = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "column_inserted", // After which the columns are inserted colIndex, // The number of the inserted numCols); } break; case nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_TABLE_COLUMN_DELETE\n")); AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); PRInt32 colIndex = tableEvent->GetIndex(); PRInt32 numCols = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "column_deleted", // After which the columns are deleted colIndex, // The number of the deleted numCols); } break; case nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER: MAI_LOG_DEBUG(("\n\nReceived: EVENT_TABLE_COLUMN_REORDER\n")); g_signal_emit_by_name(atkObj, "column_reordered"); break; case nsIAccessibleEvent::EVENT_SECTION_CHANGED: MAI_LOG_DEBUG(("\n\nReceived: EVENT_SECTION_CHANGED\n")); g_signal_emit_by_name(atkObj, "visible_data_changed"); break; case nsIAccessibleEvent::EVENT_SHOW: return FireAtkShowHideEvent(aEvent, atkObj, true); case nsIAccessibleEvent::EVENT_HIDE: 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: MAI_LOG_DEBUG(("\n\nReceived: EVENT_MENU_START\n")); break; case nsIAccessibleEvent::EVENT_MENU_END: MAI_LOG_DEBUG(("\n\nReceived: EVENT_MENU_END\n")); break; case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_ACTIVATED\n")); 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: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_DEACTIVATED\n")); 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: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_MAXIMIZE\n")); guint id = g_signal_lookup ("maximize", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_MINIMIZE\n")); guint id = g_signal_lookup ("minimize", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_WINDOW_RESTORE: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_WINDOW_RESTORE\n")); guint id = g_signal_lookup ("restore", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_LOAD_COMPLETE\n")); g_signal_emit_by_name (atkObj, "load_complete"); } break; case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_RELOAD\n")); g_signal_emit_by_name (atkObj, "reload"); } break; case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED: { MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_LOAD_STOPPED\n")); g_signal_emit_by_name (atkObj, "load_stopped"); } break; case nsIAccessibleEvent::EVENT_MENUPOPUP_START: 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, true); atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, true); break; case nsIAccessibleEvent::EVENT_MENUPOPUP_END: MAI_LOG_DEBUG(("\n\nReceived: EVENT_MENUPOPUP_END\n")); 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) { 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; }