/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AccEvent.h" #include "nsAccUtils.h" #include "xpcAccEvents.h" #include "States.h" #include "TextRange.h" #include "xpcAccessibleDocument.h" #include "xpcAccessibleTextRange.h" #include "mozilla/dom/Selection.h" #include "mozilla/dom/UserActivation.h" #include "nsComponentManagerUtils.h" #include "nsIMutableArray.h" using namespace mozilla; using namespace mozilla::a11y; static_assert(static_cast(eNoUserInput) == false && static_cast(eFromUserInput) == true, "EIsFromUserInput cannot be casted to bool"); //////////////////////////////////////////////////////////////////////////////// // AccEvent //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // AccEvent constructors AccEvent::AccEvent(uint32_t aEventType, LocalAccessible* aAccessible, EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) : mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible) { if (aIsFromUserInput == eAutoDetect) { mIsFromUserInput = dom::UserActivation::IsHandlingUserInput(); } else { mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false; } } //////////////////////////////////////////////////////////////////////////////// // AccEvent cycle collection NS_IMPL_CYCLE_COLLECTION_CLASS(AccEvent) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AccEvent) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessible) if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) { tmEvent->SetNextEvent(nullptr); tmEvent->SetPrevEvent(nullptr); } NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AccEvent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessible) if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) { CycleCollectionNoteChild(cb, tmEvent->NextEvent(), "mNext"); CycleCollectionNoteChild(cb, tmEvent->PrevEvent(), "mPrevEvent"); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // AccTextChangeEvent //////////////////////////////////////////////////////////////////////////////// // Note: we pass in eAllowDupes to the base class because we don't support text // events coalescence. We fire delayed text change events in DocAccessible but // we continue to base the event off the accessible object rather than just the // node. This means we won't try to create an accessible based on the node when // we are ready to fire the event and so we will no longer assert at that point // if the node was removed from the document. Either way, the AT won't work with // a defunct accessible so the behaviour should be equivalent. AccTextChangeEvent::AccTextChangeEvent(LocalAccessible* aAccessible, int32_t aStart, const nsAString& aModifiedText, bool aIsInserted, EIsFromUserInput aIsFromUserInput) : AccEvent( aIsInserted ? static_cast(nsIAccessibleEvent::EVENT_TEXT_INSERTED) : static_cast(nsIAccessibleEvent::EVENT_TEXT_REMOVED), aAccessible, aIsFromUserInput, eAllowDupes), mStart(aStart), mIsInserted(aIsInserted), mModifiedText(aModifiedText) { // XXX We should use IsFromUserInput here, but that isn't always correct // when the text change isn't related to content insertion or removal. mIsFromUserInput = mAccessible->State() & (states::FOCUSED | states::EDITABLE); } //////////////////////////////////////////////////////////////////////////////// // AccHideEvent //////////////////////////////////////////////////////////////////////////////// AccHideEvent::AccHideEvent(LocalAccessible* aTarget, bool aNeedsShutdown) : AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget), mNeedsShutdown(aNeedsShutdown) { mNextSibling = mAccessible->LocalNextSibling(); mPrevSibling = mAccessible->LocalPrevSibling(); } //////////////////////////////////////////////////////////////////////////////// // AccShowEvent //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // AccTextSelChangeEvent //////////////////////////////////////////////////////////////////////////////// AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget, dom::Selection* aSelection, int32_t aReason, int32_t aGranularity) : AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget, eAutoDetect, eCoalesceTextSelChange), mSel(aSelection), mReason(aReason), mGranularity(aGranularity) {} AccTextSelChangeEvent::~AccTextSelChangeEvent() {} bool AccTextSelChangeEvent::IsCaretMoveOnly() const { return mSel->RangeCount() == 1 && mSel->IsCollapsed() && ((mReason & (nsISelectionListener::COLLAPSETOSTART_REASON | nsISelectionListener::COLLAPSETOEND_REASON)) == 0); } void AccTextSelChangeEvent::SelectionRanges( nsTArray* aRanges) const { TextRange::TextRangesFromSelection(mSel, aRanges); } //////////////////////////////////////////////////////////////////////////////// // AccSelChangeEvent //////////////////////////////////////////////////////////////////////////////// AccSelChangeEvent::AccSelChangeEvent(LocalAccessible* aWidget, LocalAccessible* aItem, SelChangeType aSelChangeType) : AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange), mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType), mPreceedingCount(0), mPackedEvent(nullptr) { if (aSelChangeType == eSelectionAdd) { if (mWidget->GetSelectedItem(1)) { mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD; } else { mEventType = nsIAccessibleEvent::EVENT_SELECTION; } } else { mEventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE; } } already_AddRefed a11y::MakeXPCEvent(AccEvent* aEvent) { DocAccessible* doc = aEvent->Document(); LocalAccessible* acc = aEvent->GetAccessible(); nsINode* node = acc->GetNode(); bool fromUser = aEvent->IsFromUserInput(); uint32_t type = aEvent->GetEventType(); uint32_t eventGroup = aEvent->GetEventGroups(); nsCOMPtr 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, ToXPC(acc), ToXPCDocument(doc), node, 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, ToXPC(acc), ToXPCDocument(doc), node, 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, ToXPC(acc), ToXPCDocument(doc), node, fromUser, ToXPC(hideEvent->TargetParent()), ToXPC(hideEvent->TargetNextSibling()), ToXPC(hideEvent->TargetPrevSibling())); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eCaretMoveEvent)) { AccCaretMoveEvent* cm = downcast_accEvent(aEvent); xpEvent = new xpcAccCaretMoveEvent( type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, cm->GetCaretOffset(), cm->IsSelectionCollapsed(), cm->IsAtEndOfLine(), cm->GetGranularity()); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eTextSelChangeEvent)) { AccTextSelChangeEvent* tsc = downcast_accEvent(aEvent); AutoTArray ranges; tsc->SelectionRanges(&ranges); nsCOMPtr xpcRanges = do_CreateInstance(NS_ARRAY_CONTRACTID); uint32_t len = ranges.Length(); for (uint32_t idx = 0; idx < len; idx++) { xpcRanges->AppendElement(new xpcAccessibleTextRange(ranges[idx])); } xpEvent = new xpcAccTextSelectionChangeEvent( type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, xpcRanges); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eObjectAttrChangedEvent)) { AccObjectAttrChangedEvent* oac = downcast_accEvent(aEvent); nsString attribute; oac->GetAttribute()->ToString(attribute); xpEvent = new xpcAccObjectAttributeChangedEvent( type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, attribute); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eScrollingEvent)) { AccScrollingEvent* sa = downcast_accEvent(aEvent); xpEvent = new xpcAccScrollingEvent( type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, sa->ScrollX(), sa->ScrollY(), sa->MaxScrollX(), sa->MaxScrollY()); return xpEvent.forget(); } if (eventGroup & (1 << AccEvent::eAnnouncementEvent)) { AccAnnouncementEvent* aa = downcast_accEvent(aEvent); xpEvent = new xpcAccAnnouncementEvent(type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, aa->Announcement(), aa->Priority()); return xpEvent.forget(); } xpEvent = new xpcAccEvent(type, ToXPC(acc), ToXPCDocument(doc), node, fromUser); return xpEvent.forget(); }