Bug 1777562 [wpt PR 34663] - [FedCM] Rename FederatedCredential to IdentityCredential...
[gecko.git] / accessible / mac / AccessibleWrap.mm
blob47af7ee5b1233aa400a4a1b8415301d452bf0434
1 /* clang-format off */
2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* clang-format on */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "DocAccessibleWrap.h"
9 #include "nsObjCExceptions.h"
10 #include "nsCocoaUtils.h"
12 #include "LocalAccessible-inl.h"
13 #include "nsAccUtils.h"
14 #include "Role.h"
15 #include "TextRange.h"
16 #include "gfxPlatform.h"
18 #import "MOXLandmarkAccessibles.h"
19 #import "MOXMathAccessibles.h"
20 #import "MOXTextMarkerDelegate.h"
21 #import "MOXWebAreaAccessible.h"
22 #import "mozAccessible.h"
23 #import "mozActionElements.h"
24 #import "mozHTMLAccessible.h"
25 #import "mozSelectableElements.h"
26 #import "mozTableAccessible.h"
27 #import "mozTextAccessible.h"
29 using namespace mozilla;
30 using namespace mozilla::a11y;
32 AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc)
33     : LocalAccessible(aContent, aDoc),
34       mNativeObject(nil),
35       mNativeInited(false) {
36   if (aContent && aContent->IsElement() && aDoc) {
37     // Check if this accessible is a live region and queue it
38     // it for dispatching an event after it has been inserted.
39     DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(aDoc);
40     static const dom::Element::AttrValuesArray sLiveRegionValues[] = {
41         nsGkAtoms::OFF, nsGkAtoms::polite, nsGkAtoms::assertive, nullptr};
42     int32_t attrValue = aContent->AsElement()->FindAttrValueIn(
43         kNameSpaceID_None, nsGkAtoms::aria_live, sLiveRegionValues,
44         eIgnoreCase);
45     if (attrValue == 0) {
46       // aria-live is "off", do nothing.
47     } else if (attrValue > 0) {
48       // aria-live attribute is polite or assertive. It's live!
49       doc->QueueNewLiveRegion(this);
50     } else if (const nsRoleMapEntry* roleMap =
51                    aria::GetRoleMap(aContent->AsElement())) {
52       // aria role defines it as a live region. It's live!
53       if (roleMap->liveAttRule == ePoliteLiveAttr ||
54           roleMap->liveAttRule == eAssertiveLiveAttr) {
55         doc->QueueNewLiveRegion(this);
56       }
57     } else if (nsStaticAtom* value = GetAccService()->MarkupAttribute(
58                    aContent, nsGkAtoms::aria_live)) {
59       // HTML element defines it as a live region. It's live!
60       if (value == nsGkAtoms::polite || value == nsGkAtoms::assertive) {
61         doc->QueueNewLiveRegion(this);
62       }
63     }
64   }
67 AccessibleWrap::~AccessibleWrap() {}
69 mozAccessible* AccessibleWrap::GetNativeObject() {
70   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
72   if (!mNativeInited && !mNativeObject) {
73     // We don't creat OSX accessibles for xul tooltips, defunct accessibles,
74     // <br> (whitespace) elements, or pruned children.
75     //
76     // To maintain a scripting environment where the XPCOM accessible hierarchy
77     // look the same on all platforms, we still let the C++ objects be created
78     // though.
79     if (!IsXULTooltip() && !IsDefunct() && Role() != roles::WHITESPACE) {
80       mNativeObject = [[GetNativeType() alloc] initWithAccessible:this];
81     }
82   }
84   mNativeInited = true;
86   return mNativeObject;
88   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
91 void AccessibleWrap::GetNativeInterface(void** aOutInterface) {
92   *aOutInterface = static_cast<void*>(GetNativeObject());
95 // overridden in subclasses to create the right kind of object. by default we
96 // create a generic 'mozAccessible' node.
97 Class AccessibleWrap::GetNativeType() {
98   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
100   if (IsXULTabpanels()) {
101     return [mozPaneAccessible class];
102   }
104   if (IsTable()) {
105     return [mozTableAccessible class];
106   }
108   if (IsTableRow()) {
109     return [mozTableRowAccessible class];
110   }
112   if (IsTableCell()) {
113     return [mozTableCellAccessible class];
114   }
116   if (IsDoc()) {
117     return [MOXWebAreaAccessible class];
118   }
120   return GetTypeFromRole(Role());
122   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
125 // this method is very important. it is fired when an accessible object "dies".
126 // after this point the object might still be around (because some 3rd party
127 // still has a ref to it), but it is in fact 'dead'.
128 void AccessibleWrap::Shutdown() {
129   // this ensure we will not try to re-create the native object.
130   mNativeInited = true;
132   // we really intend to access the member directly.
133   if (mNativeObject) {
134     [mNativeObject expire];
135     [mNativeObject release];
136     mNativeObject = nil;
137   }
139   LocalAccessible::Shutdown();
142 nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
143   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
145   nsresult rv = LocalAccessible::HandleAccEvent(aEvent);
146   NS_ENSURE_SUCCESS(rv, rv);
148   if (IsDefunct()) {
149     // The accessible can become defunct after their events are handled.
150     return NS_OK;
151   }
153   uint32_t eventType = aEvent->GetEventType();
155   if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
156     DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(Document());
157     doc->ProcessNewLiveRegions();
158   }
160   if (IPCAccessibilityActive()) {
161     return NS_OK;
162   }
164   LocalAccessible* eventTarget = nullptr;
166   switch (eventType) {
167     case nsIAccessibleEvent::EVENT_SELECTION:
168     case nsIAccessibleEvent::EVENT_SELECTION_ADD:
169     case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: {
170       AccSelChangeEvent* selEvent = downcast_accEvent(aEvent);
171       // The "widget" is the selected widget's container. In OSX
172       // it is the target of the selection changed event.
173       eventTarget = selEvent->Widget();
174       break;
175     }
176     case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
177     case nsIAccessibleEvent::EVENT_TEXT_REMOVED: {
178       LocalAccessible* acc = aEvent->GetAccessible();
179       // If there is a text input ancestor, use it as the event source.
180       while (acc && GetTypeFromRole(acc->Role()) != [mozTextAccessible class]) {
181         acc = acc->LocalParent();
182       }
183       eventTarget = acc ? acc : aEvent->GetAccessible();
184       break;
185     }
186     default:
187       eventTarget = aEvent->GetAccessible();
188       break;
189   }
191   mozAccessible* nativeAcc = nil;
192   eventTarget->GetNativeInterface((void**)&nativeAcc);
193   if (!nativeAcc) {
194     return NS_ERROR_FAILURE;
195   }
197   switch (eventType) {
198     case nsIAccessibleEvent::EVENT_STATE_CHANGE: {
199       AccStateChangeEvent* event = downcast_accEvent(aEvent);
200       [nativeAcc stateChanged:event->GetState()
201                     isEnabled:event->IsStateEnabled()];
202       break;
203     }
205     case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: {
206       MOXTextMarkerDelegate* delegate =
207           [MOXTextMarkerDelegate getOrCreateForDoc:aEvent->Document()];
208       AccTextSelChangeEvent* event = downcast_accEvent(aEvent);
209       AutoTArray<TextRange, 1> ranges;
210       event->SelectionRanges(&ranges);
212       if (ranges.Length()) {
213         // Cache selection in delegate.
214         [delegate setSelectionFrom:ranges[0].StartContainer()
215                                 at:ranges[0].StartOffset()
216                                 to:ranges[0].EndContainer()
217                                 at:ranges[0].EndOffset()];
218       }
220       [nativeAcc handleAccessibleEvent:eventType];
221       break;
222     }
224     case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
225       AccCaretMoveEvent* event = downcast_accEvent(aEvent);
226       int32_t caretOffset = event->GetCaretOffset();
227       MOXTextMarkerDelegate* delegate =
228           [MOXTextMarkerDelegate getOrCreateForDoc:aEvent->Document()];
229       [delegate setCaretOffset:eventTarget
230                             at:caretOffset
231                moveGranularity:event->GetGranularity()];
232       if (event->IsSelectionCollapsed()) {
233         // If the selection is collapsed, invalidate our text selection cache.
234         [delegate setSelectionFrom:eventTarget
235                                 at:caretOffset
236                                 to:eventTarget
237                                 at:caretOffset];
238       }
240       if (mozTextAccessible* textAcc = static_cast<mozTextAccessible*>(
241               [nativeAcc moxEditableAncestor])) {
242         [textAcc
243             handleAccessibleEvent:nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED];
244       } else {
245         [nativeAcc
246             handleAccessibleEvent:nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED];
247       }
248       break;
249     }
251     case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
252     case nsIAccessibleEvent::EVENT_TEXT_REMOVED: {
253       AccTextChangeEvent* tcEvent = downcast_accEvent(aEvent);
254       [nativeAcc handleAccessibleTextChangeEvent:nsCocoaUtils::ToNSString(
255                                                      tcEvent->ModifiedText())
256                                         inserted:tcEvent->IsTextInserted()
257                                      inContainer:aEvent->GetAccessible()
258                                               at:tcEvent->GetStartOffset()];
259       break;
260     }
262     case nsIAccessibleEvent::EVENT_ALERT:
263     case nsIAccessibleEvent::EVENT_FOCUS:
264     case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE:
265     case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
266     case nsIAccessibleEvent::EVENT_MENUPOPUP_START:
267     case nsIAccessibleEvent::EVENT_MENUPOPUP_END:
268     case nsIAccessibleEvent::EVENT_REORDER:
269     case nsIAccessibleEvent::EVENT_SELECTION:
270     case nsIAccessibleEvent::EVENT_SELECTION_ADD:
271     case nsIAccessibleEvent::EVENT_SELECTION_REMOVE:
272     case nsIAccessibleEvent::EVENT_LIVE_REGION_ADDED:
273     case nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED:
274     case nsIAccessibleEvent::EVENT_NAME_CHANGE:
275     case nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED:
276     case nsIAccessibleEvent::EVENT_TABLE_STYLING_CHANGED:
277       [nativeAcc handleAccessibleEvent:eventType];
278       break;
280     default:
281       break;
282   }
284   return NS_OK;
286   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
289 bool AccessibleWrap::ApplyPostFilter(const EWhichPostFilter& aSearchKey,
290                                      const nsString& aSearchText) {
291   // We currently only support the eContainsText post filter.
292   MOZ_ASSERT(aSearchKey == EWhichPostFilter::eContainsText,
293              "Only search text supported");
294   nsAutoString name;
295   Name(name);
296   return name.Find(aSearchText, true) != kNotFound;
299 ////////////////////////////////////////////////////////////////////////////////
300 // AccessibleWrap protected
302 Class a11y::GetTypeFromRole(roles::Role aRole) {
303   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
305   switch (aRole) {
306     case roles::COMBOBOX:
307       return [mozPopupButtonAccessible class];
309     case roles::PUSHBUTTON:
310       return [mozButtonAccessible class];
312     case roles::PAGETAB:
313       return [mozTabAccessible class];
315     case roles::CHECKBUTTON:
316     case roles::TOGGLE_BUTTON:
317     case roles::SWITCH:
318       return [mozCheckboxAccessible class];
320     case roles::RADIOBUTTON:
321       return [mozRadioButtonAccessible class];
323     case roles::SPINBUTTON:
324     case roles::SLIDER:
325       return [mozIncrementableAccessible class];
327     case roles::HEADING:
328       return [mozHeadingAccessible class];
330     case roles::PAGETABLIST:
331       return [mozTabGroupAccessible class];
333     case roles::ENTRY:
334     case roles::CAPTION:
335     case roles::ACCEL_LABEL:
336     case roles::EDITCOMBOBOX:
337     case roles::PASSWORD_TEXT:
338       // normal textfield (static or editable)
339       return [mozTextAccessible class];
341     case roles::TEXT_LEAF:
342     case roles::STATICTEXT:
343       return [mozTextLeafAccessible class];
345     case roles::LANDMARK:
346       return [MOXLandmarkAccessible class];
348     case roles::LINK:
349       return [mozLinkAccessible class];
351     case roles::LISTBOX:
352       return [mozListboxAccessible class];
354     case roles::LISTITEM:
355       return [MOXListItemAccessible class];
357     case roles::OPTION: {
358       return [mozOptionAccessible class];
359     }
361     case roles::RICH_OPTION: {
362       return [mozSelectableChildAccessible class];
363     }
365     case roles::COMBOBOX_LIST:
366     case roles::MENUBAR:
367     case roles::MENUPOPUP: {
368       return [mozMenuAccessible class];
369     }
371     case roles::COMBOBOX_OPTION:
372     case roles::PARENT_MENUITEM:
373     case roles::MENUITEM: {
374       return [mozMenuItemAccessible class];
375     }
377     case roles::MATHML_ROOT:
378       return [MOXMathRootAccessible class];
380     case roles::MATHML_SQUARE_ROOT:
381       return [MOXMathSquareRootAccessible class];
383     case roles::MATHML_FRACTION:
384       return [MOXMathFractionAccessible class];
386     case roles::MATHML_SUB:
387     case roles::MATHML_SUP:
388     case roles::MATHML_SUB_SUP:
389       return [MOXMathSubSupAccessible class];
391     case roles::MATHML_UNDER:
392     case roles::MATHML_OVER:
393     case roles::MATHML_UNDER_OVER:
394       return [MOXMathUnderOverAccessible class];
396     case roles::OUTLINE:
397     case roles::TREE_TABLE:
398       return [mozOutlineAccessible class];
400     case roles::OUTLINEITEM:
401       return [mozOutlineRowAccessible class];
403     default:
404       return [mozAccessible class];
405   }
407   return nil;
409   NS_OBJC_END_TRY_BLOCK_RETURN(nil);