1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "nsAccUtils.h"
10 #include "xpcAccEvents.h"
12 #include "TextRange.h"
13 #include "xpcAccessibleDocument.h"
14 #include "xpcAccessibleTextRange.h"
16 #include "mozilla/dom/Selection.h"
17 #include "mozilla/dom/UserActivation.h"
19 #include "nsComponentManagerUtils.h"
20 #include "nsIMutableArray.h"
22 using namespace mozilla
;
23 using namespace mozilla::a11y
;
25 static_assert(static_cast<bool>(eNoUserInput
) == false &&
26 static_cast<bool>(eFromUserInput
) == true,
27 "EIsFromUserInput cannot be casted to bool");
29 ////////////////////////////////////////////////////////////////////////////////
31 ////////////////////////////////////////////////////////////////////////////////
33 ////////////////////////////////////////////////////////////////////////////////
34 // AccEvent constructors
36 AccEvent::AccEvent(uint32_t aEventType
, LocalAccessible
* aAccessible
,
37 EIsFromUserInput aIsFromUserInput
, EEventRule aEventRule
)
38 : mEventType(aEventType
), mEventRule(aEventRule
), mAccessible(aAccessible
) {
39 if (aIsFromUserInput
== eAutoDetect
) {
40 mIsFromUserInput
= dom::UserActivation::IsHandlingUserInput();
42 mIsFromUserInput
= aIsFromUserInput
== eFromUserInput
? true : false;
46 ////////////////////////////////////////////////////////////////////////////////
47 // AccEvent cycle collection
49 NS_IMPL_CYCLE_COLLECTION_CLASS(AccEvent
)
51 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AccEvent
)
52 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessible
)
53 if (AccTreeMutationEvent
* tmEvent
= downcast_accEvent(tmp
)) {
54 tmEvent
->SetNextEvent(nullptr);
55 tmEvent
->SetPrevEvent(nullptr);
57 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
59 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AccEvent
)
60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessible
)
61 if (AccTreeMutationEvent
* tmEvent
= downcast_accEvent(tmp
)) {
62 CycleCollectionNoteChild(cb
, tmEvent
->NextEvent(), "mNext");
63 CycleCollectionNoteChild(cb
, tmEvent
->PrevEvent(), "mPrevEvent");
65 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
67 ////////////////////////////////////////////////////////////////////////////////
68 ////////////////////////////////////////////////////////////////////////////////
70 ////////////////////////////////////////////////////////////////////////////////
72 // Note: we pass in eAllowDupes to the base class because we don't support text
73 // events coalescence. We fire delayed text change events in DocAccessible but
74 // we continue to base the event off the accessible object rather than just the
75 // node. This means we won't try to create an accessible based on the node when
76 // we are ready to fire the event and so we will no longer assert at that point
77 // if the node was removed from the document. Either way, the AT won't work with
78 // a defunct accessible so the behaviour should be equivalent.
79 AccTextChangeEvent::AccTextChangeEvent(LocalAccessible
* aAccessible
,
81 const nsAString
& aModifiedText
,
83 EIsFromUserInput aIsFromUserInput
)
86 ? static_cast<uint32_t>(nsIAccessibleEvent::EVENT_TEXT_INSERTED
)
87 : static_cast<uint32_t>(nsIAccessibleEvent::EVENT_TEXT_REMOVED
),
88 aAccessible
, aIsFromUserInput
, eAllowDupes
),
90 mIsInserted(aIsInserted
),
91 mModifiedText(aModifiedText
) {
92 // XXX We should use IsFromUserInput here, but that isn't always correct
93 // when the text change isn't related to content insertion or removal.
95 mAccessible
->State() & (states::FOCUSED
| states::EDITABLE
);
98 ////////////////////////////////////////////////////////////////////////////////
100 ////////////////////////////////////////////////////////////////////////////////
102 AccHideEvent::AccHideEvent(LocalAccessible
* aTarget
, bool aNeedsShutdown
)
103 : AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE
, aTarget
),
104 mNeedsShutdown(aNeedsShutdown
) {
105 mNextSibling
= mAccessible
->LocalNextSibling();
106 mPrevSibling
= mAccessible
->LocalPrevSibling();
109 ////////////////////////////////////////////////////////////////////////////////
111 ////////////////////////////////////////////////////////////////////////////////
113 ////////////////////////////////////////////////////////////////////////////////
114 // AccTextSelChangeEvent
115 ////////////////////////////////////////////////////////////////////////////////
117 AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible
* aTarget
,
118 dom::Selection
* aSelection
,
120 int32_t aGranularity
)
121 : AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED
, aTarget
,
122 eAutoDetect
, eCoalesceTextSelChange
),
125 mGranularity(aGranularity
) {}
127 AccTextSelChangeEvent::~AccTextSelChangeEvent() {}
129 bool AccTextSelChangeEvent::IsCaretMoveOnly() const {
130 return mSel
->RangeCount() == 1 && mSel
->IsCollapsed() &&
131 ((mReason
& (nsISelectionListener::COLLAPSETOSTART_REASON
|
132 nsISelectionListener::COLLAPSETOEND_REASON
)) == 0);
135 void AccTextSelChangeEvent::SelectionRanges(
136 nsTArray
<TextRange
>* aRanges
) const {
137 TextRange::TextRangesFromSelection(mSel
, aRanges
);
140 ////////////////////////////////////////////////////////////////////////////////
142 ////////////////////////////////////////////////////////////////////////////////
144 AccSelChangeEvent::AccSelChangeEvent(LocalAccessible
* aWidget
,
145 LocalAccessible
* aItem
,
146 SelChangeType aSelChangeType
)
147 : AccEvent(0, aItem
, eAutoDetect
, eCoalesceSelectionChange
),
150 mSelChangeType(aSelChangeType
),
152 mPackedEvent(nullptr) {
153 if (aSelChangeType
== eSelectionAdd
) {
154 if (mWidget
->GetSelectedItem(1)) {
155 mEventType
= nsIAccessibleEvent::EVENT_SELECTION_ADD
;
157 mEventType
= nsIAccessibleEvent::EVENT_SELECTION
;
160 mEventType
= nsIAccessibleEvent::EVENT_SELECTION_REMOVE
;
164 ////////////////////////////////////////////////////////////////////////////////
166 ////////////////////////////////////////////////////////////////////////////////
168 AccVCChangeEvent::AccVCChangeEvent(LocalAccessible
* aAccessible
,
169 LocalAccessible
* aOldAccessible
,
170 LocalAccessible
* aNewAccessible
,
172 EIsFromUserInput aIsFromUserInput
)
173 : AccEvent(::nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED
, aAccessible
,
175 mOldAccessible(aOldAccessible
),
176 mNewAccessible(aNewAccessible
),
179 already_AddRefed
<nsIAccessibleEvent
> a11y::MakeXPCEvent(AccEvent
* aEvent
) {
180 DocAccessible
* doc
= aEvent
->Document();
181 LocalAccessible
* acc
= aEvent
->GetAccessible();
182 nsINode
* node
= acc
->GetNode();
183 bool fromUser
= aEvent
->IsFromUserInput();
184 uint32_t type
= aEvent
->GetEventType();
185 uint32_t eventGroup
= aEvent
->GetEventGroups();
186 nsCOMPtr
<nsIAccessibleEvent
> xpEvent
;
188 if (eventGroup
& (1 << AccEvent::eStateChangeEvent
)) {
189 AccStateChangeEvent
* sc
= downcast_accEvent(aEvent
);
191 uint32_t state
= nsAccUtils::To32States(sc
->GetState(), &extra
);
192 xpEvent
= new xpcAccStateChangeEvent(type
, ToXPC(acc
), ToXPCDocument(doc
),
193 node
, fromUser
, state
, extra
,
194 sc
->IsStateEnabled());
195 return xpEvent
.forget();
198 if (eventGroup
& (1 << AccEvent::eTextChangeEvent
)) {
199 AccTextChangeEvent
* tc
= downcast_accEvent(aEvent
);
201 tc
->GetModifiedText(text
);
202 xpEvent
= new xpcAccTextChangeEvent(
203 type
, ToXPC(acc
), ToXPCDocument(doc
), node
, fromUser
,
204 tc
->GetStartOffset(), tc
->GetLength(), tc
->IsTextInserted(), text
);
205 return xpEvent
.forget();
208 if (eventGroup
& (1 << AccEvent::eHideEvent
)) {
209 AccHideEvent
* hideEvent
= downcast_accEvent(aEvent
);
210 xpEvent
= new xpcAccHideEvent(type
, ToXPC(acc
), ToXPCDocument(doc
), node
,
211 fromUser
, ToXPC(hideEvent
->TargetParent()),
212 ToXPC(hideEvent
->TargetNextSibling()),
213 ToXPC(hideEvent
->TargetPrevSibling()));
214 return xpEvent
.forget();
217 if (eventGroup
& (1 << AccEvent::eCaretMoveEvent
)) {
218 AccCaretMoveEvent
* cm
= downcast_accEvent(aEvent
);
219 xpEvent
= new xpcAccCaretMoveEvent(
220 type
, ToXPC(acc
), ToXPCDocument(doc
), node
, fromUser
,
221 cm
->GetCaretOffset(), cm
->IsSelectionCollapsed(), cm
->IsAtEndOfLine(),
222 cm
->GetGranularity());
223 return xpEvent
.forget();
226 if (eventGroup
& (1 << AccEvent::eTextSelChangeEvent
)) {
227 AccTextSelChangeEvent
* tsc
= downcast_accEvent(aEvent
);
228 AutoTArray
<TextRange
, 1> ranges
;
229 tsc
->SelectionRanges(&ranges
);
231 nsCOMPtr
<nsIMutableArray
> xpcRanges
=
232 do_CreateInstance(NS_ARRAY_CONTRACTID
);
233 uint32_t len
= ranges
.Length();
234 for (uint32_t idx
= 0; idx
< len
; idx
++) {
235 xpcRanges
->AppendElement(new xpcAccessibleTextRange(ranges
[idx
]));
238 xpEvent
= new xpcAccTextSelectionChangeEvent(
239 type
, ToXPC(acc
), ToXPCDocument(doc
), node
, fromUser
, xpcRanges
);
240 return xpEvent
.forget();
243 if (eventGroup
& (1 << AccEvent::eVirtualCursorChangeEvent
)) {
244 AccVCChangeEvent
* vcc
= downcast_accEvent(aEvent
);
245 xpEvent
= new xpcAccVirtualCursorChangeEvent(
246 type
, ToXPC(acc
), ToXPCDocument(doc
), node
, fromUser
,
247 ToXPC(vcc
->OldAccessible()), ToXPC(vcc
->NewAccessible()),
249 return xpEvent
.forget();
252 if (eventGroup
& (1 << AccEvent::eObjectAttrChangedEvent
)) {
253 AccObjectAttrChangedEvent
* oac
= downcast_accEvent(aEvent
);
255 oac
->GetAttribute()->ToString(attribute
);
256 xpEvent
= new xpcAccObjectAttributeChangedEvent(
257 type
, ToXPC(acc
), ToXPCDocument(doc
), node
, fromUser
, attribute
);
258 return xpEvent
.forget();
261 if (eventGroup
& (1 << AccEvent::eScrollingEvent
)) {
262 AccScrollingEvent
* sa
= downcast_accEvent(aEvent
);
263 xpEvent
= new xpcAccScrollingEvent(
264 type
, ToXPC(acc
), ToXPCDocument(doc
), node
, fromUser
, sa
->ScrollX(),
265 sa
->ScrollY(), sa
->MaxScrollX(), sa
->MaxScrollY());
266 return xpEvent
.forget();
269 if (eventGroup
& (1 << AccEvent::eAnnouncementEvent
)) {
270 AccAnnouncementEvent
* aa
= downcast_accEvent(aEvent
);
271 xpEvent
= new xpcAccAnnouncementEvent(type
, ToXPC(acc
), ToXPCDocument(doc
),
272 node
, fromUser
, aa
->Announcement(),
274 return xpEvent
.forget();
278 new xpcAccEvent(type
, ToXPC(acc
), ToXPCDocument(doc
), node
, fromUser
);
279 return xpEvent
.forget();