nsresult AccessibleWrap::FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject* aObject) { AccTextChangeEvent* event = downcast_accEvent(aEvent); NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); int32_t start = event->GetStartOffset(); uint32_t length = event->GetLength(); bool isInserted = event->IsTextInserted(); bool isFromUserInput = aEvent->IsFromUserInput(); if (gAvailableAtkSignals == eUnknown) gAvailableAtkSignals = g_signal_lookup("text-insert", G_OBJECT_TYPE(aObject)) ? eHaveNewAtkTextSignals : eNoNewAtkSignals; if (gAvailableAtkSignals == eNoNewAtkSignals) { // XXX remove this code and the gHaveNewTextSignals check when we can // stop supporting old atk since it doesn't really work anyway // see bug 619002 const char* signal_name = oldTextChangeStrings[isFromUserInput][isInserted]; g_signal_emit_by_name(aObject, signal_name, start, length); } else { nsAutoString text; event->GetModifiedText(text); const char* signal_name = textChangedStrings[isFromUserInput][isInserted]; g_signal_emit_by_name(aObject, signal_name, start, length, NS_ConvertUTF16toUTF8(text).get()); } 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(); }
void EventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent, AccHideEvent* aThisEvent) { // XXX: we need a way to ignore SplitNode and JoinNode() when they do not // affect the text within the hypertext. AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent; if (!textEvent) return; if (aThisEvent->mNextSibling == aTailEvent->mAccessible) { aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText); } else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) { uint32_t oldLen = textEvent->GetLength(); aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText); textEvent->mStart -= textEvent->GetLength() - oldLen; } aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent); }
nsresult HyperTextAccessibleWrap::HandleAccEvent(AccEvent* aEvent) { uint32_t eventType = aEvent->GetEventType(); if (eventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED || eventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED) { Accessible* accessible = aEvent->GetAccessible(); if (accessible && accessible->IsHyperText()) { AccTextChangeEvent* event = downcast_accEvent(aEvent); HyperTextAccessibleWrap* text = static_cast<HyperTextAccessibleWrap*>(accessible->AsHyperText()); ia2AccessibleText::UpdateTextChangeData(text, event->IsTextInserted(), event->ModifiedText(), event->GetStartOffset(), event->GetLength()); } } return HyperTextAccessible::HandleAccEvent(aEvent); }
nsresult AccessibleWrap::FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject* aObject) { MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_REMOVED/INSERTED\n")); AccTextChangeEvent* event = downcast_accEvent(aEvent); NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); PRInt32 start = event->GetStartOffset(); PRUint32 length = event->GetLength(); bool isInserted = event->IsTextInserted(); bool isFromUserInput = aEvent->IsFromUserInput(); char* signal_name = nsnull; if (gAvailableAtkSignals == eUnknown) gAvailableAtkSignals = g_signal_lookup("text-insert", G_OBJECT_TYPE(aObject)) ? eHaveNewAtkTextSignals : eNoNewAtkSignals; if (gAvailableAtkSignals == eNoNewAtkSignals) { // XXX remove this code and the gHaveNewTextSignals check when we can // stop supporting old atk since it doesn't really work anyway // see bug 619002 signal_name = g_strconcat(isInserted ? "text_changed::insert" : "text_changed::delete", isFromUserInput ? "" : kNonUserInputEvent, NULL); g_signal_emit_by_name(aObject, signal_name, start, length); } else { nsAutoString text; event->GetModifiedText(text); signal_name = g_strconcat(isInserted ? "text-insert" : "text-remove", isFromUserInput ? "" : "::system", NULL); g_signal_emit_by_name(aObject, signal_name, start, length, NS_ConvertUTF16toUTF8(text).get()); } g_free(signal_name); return NS_OK; }
nsresult nsAccessibleWrap::FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject *aObject) { MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_REMOVED/INSERTED\n")); AccTextChangeEvent* event = downcast_accEvent(aEvent); NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); PRInt32 start = event->GetStartOffset(); PRUint32 length = event->GetLength(); PRBool isInserted = event->IsTextInserted(); PRBool isFromUserInput = aEvent->IsFromUserInput(); char *signal_name = g_strconcat(isInserted ? "text_changed::insert" : "text_changed::delete", isFromUserInput ? "" : kNonUserInputEvent, NULL); g_signal_emit_by_name(aObject, signal_name, start, length); g_free (signal_name); return NS_OK; }
nsresult HyperTextAccessibleWrap::HandleAccEvent(AccEvent* aEvent) { uint32_t eventType = aEvent->GetEventType(); if (eventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED || eventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED) { Accessible* accessible = aEvent->GetAccessible(); if (accessible && accessible->IsHyperText()) { sLastTextChangeAcc = accessible; if (!sLastTextChangeString) sLastTextChangeString = new nsString(); AccTextChangeEvent* event = downcast_accEvent(aEvent); event->GetModifiedText(*sLastTextChangeString); sLastTextChangeStart = event->GetStartOffset(); sLastTextChangeEnd = sLastTextChangeStart + event->GetLength(); sLastTextChangeWasInsert = event->IsTextInserted(); } } return HyperTextAccessible::HandleAccEvent(aEvent); }
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; }
void EventTree::Mutated(AccMutationEvent* aEv) { // If shown or hidden node is a root of previously mutated subtree, then // discard those subtree mutations as we are no longer interested in them. UniquePtr<EventTree>* node = &mFirst; while (*node) { if ((*node)->mContainer == aEv->mAccessible) { *node = Move((*node)->mNext); break; } node = &(*node)->mNext; } AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr); mDependentEvents.AppendElement(aEv); // Coalesce text change events from this hide/show event and the previous one. if (prevEvent && aEv->mEventType == prevEvent->mEventType) { if (aEv->IsHide()) { // XXX: we need a way to ignore SplitNode and JoinNode() when they do not // affect the text within the hypertext. AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent; if (prevTextEvent) { AccHideEvent* hideEvent = downcast_accEvent(aEv); AccHideEvent* prevHideEvent = downcast_accEvent(prevEvent); if (prevHideEvent->mNextSibling == hideEvent->mAccessible) { hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText); } else if (prevHideEvent->mPrevSibling == hideEvent->mAccessible) { uint32_t oldLen = prevTextEvent->GetLength(); hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText); prevTextEvent->mStart -= prevTextEvent->GetLength() - oldLen; } hideEvent->mTextChangeEvent.swap(prevEvent->mTextChangeEvent); } } else { AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent; if (prevTextEvent) { if (aEv->mAccessible->IndexInParent() == prevEvent->mAccessible->IndexInParent() + 1) { // If tail target was inserted after this target, i.e. tail target is next // sibling of this target. aEv->mAccessible->AppendTextTo(prevTextEvent->mModifiedText); } else if (aEv->mAccessible->IndexInParent() == prevEvent->mAccessible->IndexInParent() - 1) { // If tail target was inserted before this target, i.e. tail target is // previous sibling of this target. nsAutoString startText; aEv->mAccessible->AppendTextTo(startText); prevTextEvent->mModifiedText = startText + prevTextEvent->mModifiedText; prevTextEvent->mStart -= startText.Length(); } aEv->mTextChangeEvent.swap(prevEvent->mTextChangeEvent); } } } // Create a text change event caused by this hide/show event. When a node is // hidden/removed or shown/appended, the text in an ancestor hyper text will // lose or get new characters. if (aEv->mTextChangeEvent || !mContainer->IsHyperText()) { return; } nsAutoString text; aEv->mAccessible->AppendTextTo(text); if (text.IsEmpty()) { return; } int32_t offset = mContainer->AsHyperText()->GetChildOffset(aEv->mAccessible); aEv->mTextChangeEvent = new AccTextChangeEvent(mContainer, offset, text, aEv->IsShow(), aEv->mIsFromUserInput ? eFromUserInput : eNoUserInput); }
void EventTree::Mutated(AccMutationEvent* aEv) { // If shown or hidden node is a root of previously mutated subtree, then // discard those subtree mutations as we are no longer interested in them. UniquePtr<EventTree>* node = &mFirst; while (*node) { Accessible* cntr = (*node)->mContainer; while (cntr != mContainer) { if (cntr == aEv->mAccessible) { #ifdef A11Y_LOG if (logging::IsEnabled(logging::eEventTree)) { logging::MsgBegin("EVENTS_TREE", "Trim subtree"); logging::AccessibleInfo("Show/hide container", aEv->mAccessible); logging::AccessibleInfo("Trimmed subtree root", (*node)->mContainer); logging::MsgEnd(); } #endif // If the new hide is part of a move and it contains existing child // shows, then move preceding events from the child shows to the buffer, // so the ongoing show event will pick them up. if (aEv->IsHide()) { AccHideEvent* hideEv = downcast_accEvent(aEv); if (!hideEv->mNeedsShutdown) { for (uint32_t i = 0; i < (*node)->mDependentEvents.Length(); i++) { AccMutationEvent* childEv = (*node)->mDependentEvents[i]; if (childEv->IsShow()) { AccShowEvent* childShowEv = downcast_accEvent(childEv); if (childShowEv->mPrecedingEvents.Length() > 0) { Controller(mContainer)->StorePrecedingEvents( mozilla::Move(childShowEv->mPrecedingEvents)); } } } } } // If the new show contains existing child shows, then move preceding // events from the child shows to the new show. else if (aEv->IsShow()) { AccShowEvent* showEv = downcast_accEvent(aEv); for (uint32_t i = 0; (*node)->mDependentEvents.Length(); i++) { AccMutationEvent* childEv = (*node)->mDependentEvents[i]; if (childEv->IsShow()) { AccShowEvent* showChildEv = downcast_accEvent(childEv); if (showChildEv->mPrecedingEvents.Length() > 0) { #ifdef A11Y_LOG if (logging::IsEnabled(logging::eEventTree)) { logging::MsgBegin("EVENTS_TREE", "Adopt preceding events"); logging::AccessibleInfo("Parent", aEv->mAccessible); for (uint32_t j = 0; j < showChildEv->mPrecedingEvents.Length(); j++) { logging::AccessibleInfo("Adoptee", showChildEv->mPrecedingEvents[i]->mAccessible); } logging::MsgEnd(); } #endif showEv->mPrecedingEvents.AppendElements(showChildEv->mPrecedingEvents); } } } } *node = Move((*node)->mNext); break; } cntr = cntr->Parent(); } if (cntr == aEv->mAccessible) { continue; } node = &(*node)->mNext; } AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr); mDependentEvents.AppendElement(aEv); // Coalesce text change events from this hide/show event and the previous one. if (prevEvent && aEv->mEventType == prevEvent->mEventType) { if (aEv->IsHide()) { // XXX: we need a way to ignore SplitNode and JoinNode() when they do not // affect the text within the hypertext. AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent; if (prevTextEvent) { AccHideEvent* hideEvent = downcast_accEvent(aEv); AccHideEvent* prevHideEvent = downcast_accEvent(prevEvent); if (prevHideEvent->mNextSibling == hideEvent->mAccessible) { hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText); } else if (prevHideEvent->mPrevSibling == hideEvent->mAccessible) { uint32_t oldLen = prevTextEvent->GetLength(); hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText); prevTextEvent->mStart -= prevTextEvent->GetLength() - oldLen; } hideEvent->mTextChangeEvent.swap(prevEvent->mTextChangeEvent); } } else { AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent; if (prevTextEvent) { if (aEv->mAccessible->IndexInParent() == prevEvent->mAccessible->IndexInParent() + 1) { // If tail target was inserted after this target, i.e. tail target is next // sibling of this target. aEv->mAccessible->AppendTextTo(prevTextEvent->mModifiedText); } else if (aEv->mAccessible->IndexInParent() == prevEvent->mAccessible->IndexInParent() - 1) { // If tail target was inserted before this target, i.e. tail target is // previous sibling of this target. nsAutoString startText; aEv->mAccessible->AppendTextTo(startText); prevTextEvent->mModifiedText = startText + prevTextEvent->mModifiedText; prevTextEvent->mStart -= startText.Length(); } aEv->mTextChangeEvent.swap(prevEvent->mTextChangeEvent); } } } // Create a text change event caused by this hide/show event. When a node is // hidden/removed or shown/appended, the text in an ancestor hyper text will // lose or get new characters. if (aEv->mTextChangeEvent || !mContainer->IsHyperText()) { return; } nsAutoString text; aEv->mAccessible->AppendTextTo(text); if (text.IsEmpty()) { return; } int32_t offset = mContainer->AsHyperText()->GetChildOffset(aEv->mAccessible); aEv->mTextChangeEvent = new AccTextChangeEvent(mContainer, offset, text, aEv->IsShow(), aEv->mIsFromUserInput ? eFromUserInput : eNoUserInput); }