Bug 1890277: part 4) Add CSPParser support for the `trusted-types` directive, guarded...
[gecko.git] / accessible / xul / XULFormControlAccessible.cpp
blob038f93dd68ac76a9469867dfa9dd20aec73476d0
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "XULFormControlAccessible.h"
8 #include "LocalAccessible-inl.h"
9 #include "HTMLFormControlAccessible.h"
10 #include "nsAccUtils.h"
11 #include "DocAccessible.h"
12 #include "Relation.h"
13 #include "mozilla/a11y/Role.h"
14 #include "States.h"
15 #include "TreeWalker.h"
16 #include "XULMenuAccessible.h"
18 #include "nsIDOMXULButtonElement.h"
19 #include "nsIDOMXULMenuListElement.h"
20 #include "nsIDOMXULRadioGroupElement.h"
21 #include "nsIDOMXULSelectCntrlItemEl.h"
22 #include "nsIFrame.h"
23 #include "nsITextControlFrame.h"
24 #include "nsMenuPopupFrame.h"
25 #include "nsNameSpaceManager.h"
26 #include "mozilla/dom/Element.h"
28 using namespace mozilla::a11y;
30 ////////////////////////////////////////////////////////////////////////////////
31 // XULButtonAccessible
32 ////////////////////////////////////////////////////////////////////////////////
34 XULButtonAccessible::XULButtonAccessible(nsIContent* aContent,
35 DocAccessible* aDoc)
36 : AccessibleWrap(aContent, aDoc) {
37 if (ContainsMenu()) {
38 mGenericTypes |= eMenuButton;
39 } else {
40 mGenericTypes |= eButton;
44 XULButtonAccessible::~XULButtonAccessible() {}
46 ////////////////////////////////////////////////////////////////////////////////
47 // XULButtonAccessible: nsISupports
49 ////////////////////////////////////////////////////////////////////////////////
50 // XULButtonAccessible: nsIAccessible
52 bool XULButtonAccessible::HasPrimaryAction() const { return true; }
54 void XULButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
55 if (aIndex == eAction_Click) aName.AssignLiteral("press");
58 ////////////////////////////////////////////////////////////////////////////////
59 // XULButtonAccessible: LocalAccessible
61 role XULButtonAccessible::NativeRole() const {
62 // Buttons can be checked; they simply appear pressed in rather than checked.
63 // In this case, we must expose them as toggle buttons.
64 nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
65 if (xulButtonElement) {
66 nsAutoString type;
67 xulButtonElement->GetType(type);
68 if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
69 return roles::TOGGLE_BUTTON;
72 return roles::PUSHBUTTON;
75 uint64_t XULButtonAccessible::NativeState() const {
76 // Possible states: focused, focusable, unavailable(disabled).
78 // get focus and disable status from base class
79 uint64_t state = LocalAccessible::NativeState();
81 nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
82 if (xulButtonElement) {
83 // Some buttons can have their checked state set without being of type
84 // checkbox or radio. Expose the pressed state unconditionally.
85 bool checked = false;
86 xulButtonElement->GetChecked(&checked);
87 if (checked) {
88 state |= states::PRESSED;
92 if (ContainsMenu()) state |= states::HASPOPUP;
94 if (mContent->AsElement()->HasAttr(nsGkAtoms::_default)) {
95 state |= states::DEFAULT;
98 return state;
101 bool XULButtonAccessible::AttributeChangesState(nsAtom* aAttribute) {
102 if (aAttribute == nsGkAtoms::checked) {
103 return true;
105 return AccessibleWrap::AttributeChangesState(aAttribute);
108 ////////////////////////////////////////////////////////////////////////////////
109 // XULButtonAccessible: Widgets
111 bool XULButtonAccessible::IsWidget() const { return true; }
113 bool XULButtonAccessible::IsActiveWidget() const {
114 return FocusMgr()->HasDOMFocus(mContent);
117 bool XULButtonAccessible::AreItemsOperable() const {
118 if (IsMenuButton()) {
119 LocalAccessible* menuPopup = mChildren.SafeElementAt(0, nullptr);
120 if (menuPopup) {
121 nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
122 return menuPopupFrame->IsOpen();
125 return false; // no items
128 bool XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
129 // In general XUL buttons should not have accessible children. However:
130 return
131 // menu buttons can have popup accessibles (@type="menu" or
132 // columnpicker).
133 aEl->IsXULElement(nsGkAtoms::menupopup) ||
134 // A XUL button can be labelled by a direct child text node, so we need to
135 // allow that as a child so it will be picked up when computing name from
136 // subtree.
137 (aEl->IsText() && aEl->GetParent() == mContent);
140 ////////////////////////////////////////////////////////////////////////////////
141 // XULButtonAccessible protected
143 bool XULButtonAccessible::ContainsMenu() const {
144 return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
145 nsGkAtoms::menu, eCaseMatters);
148 ////////////////////////////////////////////////////////////////////////////////
149 // XULDropmarkerAccessible
150 ////////////////////////////////////////////////////////////////////////////////
152 XULDropmarkerAccessible::XULDropmarkerAccessible(nsIContent* aContent,
153 DocAccessible* aDoc)
154 : LeafAccessible(aContent, aDoc) {}
156 bool XULDropmarkerAccessible::HasPrimaryAction() const { return true; }
158 bool XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const {
159 bool isOpen = false;
161 nsIContent* parent = mContent->GetFlattenedTreeParent();
163 while (parent) {
164 nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
165 parent->AsElement()->AsXULButton();
166 if (parentButtonElement) {
167 parentButtonElement->GetOpen(&isOpen);
168 if (aToggleOpen) parentButtonElement->SetOpen(!isOpen);
169 return isOpen;
172 nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
173 parent->AsElement()->AsXULMenuList();
174 if (parentMenuListElement) {
175 parentMenuListElement->GetOpen(&isOpen);
176 if (aToggleOpen) parentMenuListElement->SetOpen(!isOpen);
177 return isOpen;
179 parent = parent->GetFlattenedTreeParent();
182 return isOpen;
185 void XULDropmarkerAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
186 aName.Truncate();
187 if (aIndex == eAction_Click) {
188 if (DropmarkerOpen(false)) {
189 aName.AssignLiteral("close");
190 } else {
191 aName.AssignLiteral("open");
196 bool XULDropmarkerAccessible::DoAction(uint8_t index) const {
197 if (index == eAction_Click) {
198 DropmarkerOpen(true); // Reverse the open attribute
199 return true;
201 return false;
204 role XULDropmarkerAccessible::NativeRole() const { return roles::PUSHBUTTON; }
206 uint64_t XULDropmarkerAccessible::NativeState() const {
207 return DropmarkerOpen(false) ? states::PRESSED : 0;
210 ////////////////////////////////////////////////////////////////////////////////
211 // XULGroupboxAccessible
212 ////////////////////////////////////////////////////////////////////////////////
214 XULGroupboxAccessible::XULGroupboxAccessible(nsIContent* aContent,
215 DocAccessible* aDoc)
216 : AccessibleWrap(aContent, aDoc) {}
218 role XULGroupboxAccessible::NativeRole() const { return roles::GROUPING; }
220 ENameValueFlag XULGroupboxAccessible::NativeName(nsString& aName) const {
221 // XXX: we use the first related accessible only.
222 LocalAccessible* label =
223 RelationByType(RelationType::LABELLED_BY).LocalNext();
224 if (label) return label->Name(aName);
226 return eNameOK;
229 Relation XULGroupboxAccessible::RelationByType(RelationType aType) const {
230 Relation rel = AccessibleWrap::RelationByType(aType);
232 // The label for xul:groupbox is generated from the first xul:label
233 if (aType == RelationType::LABELLED_BY && ChildCount() > 0) {
234 LocalAccessible* childAcc = LocalChildAt(0);
235 if (childAcc->Role() == roles::LABEL &&
236 childAcc->GetContent()->IsXULElement(nsGkAtoms::label)) {
237 rel.AppendTarget(childAcc);
241 return rel;
244 ////////////////////////////////////////////////////////////////////////////////
245 // XULRadioButtonAccessible
246 ////////////////////////////////////////////////////////////////////////////////
248 XULRadioButtonAccessible::XULRadioButtonAccessible(nsIContent* aContent,
249 DocAccessible* aDoc)
250 : RadioButtonAccessible(aContent, aDoc) {}
252 uint64_t XULRadioButtonAccessible::NativeState() const {
253 uint64_t state = LeafAccessible::NativeState();
254 state |= states::CHECKABLE;
256 nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
257 Elm()->AsXULSelectControlItem();
258 if (radioButton) {
259 bool selected = false; // Radio buttons can be selected
260 radioButton->GetSelected(&selected);
261 if (selected) {
262 state |= states::CHECKED;
266 return state;
269 uint64_t XULRadioButtonAccessible::NativeInteractiveState() const {
270 return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
273 ////////////////////////////////////////////////////////////////////////////////
274 // XULRadioButtonAccessible: Widgets
276 LocalAccessible* XULRadioButtonAccessible::ContainerWidget() const {
277 return mParent;
280 ////////////////////////////////////////////////////////////////////////////////
281 // XULRadioGroupAccessible
282 ////////////////////////////////////////////////////////////////////////////////
285 * XUL Radio Group
286 * The Radio Group proxies for the Radio Buttons themselves. The Group gets
287 * focus whereas the Buttons do not. So we only have an accessible object for
288 * this for the purpose of getting the proper RadioButton. Need this here to
289 * avoid circular reference problems when navigating the accessible tree and
290 * for getting to the radiobuttons.
293 XULRadioGroupAccessible::XULRadioGroupAccessible(nsIContent* aContent,
294 DocAccessible* aDoc)
295 : XULSelectControlAccessible(aContent, aDoc) {}
297 role XULRadioGroupAccessible::NativeRole() const { return roles::RADIO_GROUP; }
299 uint64_t XULRadioGroupAccessible::NativeInteractiveState() const {
300 // The radio group is not focusable. Sometimes the focus controller will
301 // report that it is focused. That means that the actual selected radio button
302 // should be considered focused.
303 return NativelyUnavailable() ? states::UNAVAILABLE : 0;
306 ////////////////////////////////////////////////////////////////////////////////
307 // XULRadioGroupAccessible: Widgets
309 bool XULRadioGroupAccessible::IsWidget() const { return true; }
311 bool XULRadioGroupAccessible::IsActiveWidget() const {
312 return FocusMgr()->HasDOMFocus(mContent);
315 bool XULRadioGroupAccessible::AreItemsOperable() const { return true; }
317 LocalAccessible* XULRadioGroupAccessible::CurrentItem() const {
318 if (!mSelectControl) {
319 return nullptr;
322 RefPtr<dom::Element> currentItemElm;
323 nsCOMPtr<nsIDOMXULRadioGroupElement> group =
324 mSelectControl->AsXULRadioGroup();
325 if (group) {
326 group->GetFocusedItem(getter_AddRefs(currentItemElm));
329 if (currentItemElm) {
330 DocAccessible* document = Document();
331 if (document) {
332 return document->GetAccessible(currentItemElm);
336 return nullptr;
339 void XULRadioGroupAccessible::SetCurrentItem(const LocalAccessible* aItem) {
340 if (!mSelectControl) {
341 return;
344 nsCOMPtr<dom::Element> itemElm = aItem->Elm();
345 nsCOMPtr<nsIDOMXULRadioGroupElement> group =
346 mSelectControl->AsXULRadioGroup();
347 if (group) {
348 group->SetFocusedItem(itemElm);
352 ////////////////////////////////////////////////////////////////////////////////
353 // XULStatusBarAccessible
354 ////////////////////////////////////////////////////////////////////////////////
356 XULStatusBarAccessible::XULStatusBarAccessible(nsIContent* aContent,
357 DocAccessible* aDoc)
358 : AccessibleWrap(aContent, aDoc) {}
360 role XULStatusBarAccessible::NativeRole() const { return roles::STATUSBAR; }
362 ////////////////////////////////////////////////////////////////////////////////
363 // XULToolbarButtonAccessible
364 ////////////////////////////////////////////////////////////////////////////////
366 XULToolbarButtonAccessible::XULToolbarButtonAccessible(nsIContent* aContent,
367 DocAccessible* aDoc)
368 : XULButtonAccessible(aContent, aDoc) {}
370 void XULToolbarButtonAccessible::GetPositionAndSetSize(int32_t* aPosInSet,
371 int32_t* aSetSize) {
372 int32_t setSize = 0;
373 int32_t posInSet = 0;
375 LocalAccessible* parent = LocalParent();
376 if (!parent) return;
378 uint32_t childCount = parent->ChildCount();
379 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
380 LocalAccessible* child = parent->LocalChildAt(childIdx);
381 if (IsSeparator(child)) { // end of a group of buttons
382 if (posInSet) break; // we've found our group, so we're done
384 setSize = 0; // not our group, so start a new group
386 } else {
387 setSize++; // another button in the group
389 if (child == this) posInSet = setSize; // we've found our button
393 *aPosInSet = posInSet;
394 *aSetSize = setSize;
397 bool XULToolbarButtonAccessible::IsSeparator(LocalAccessible* aAccessible) {
398 nsIContent* content = aAccessible->GetContent();
399 return content && content->IsAnyOfXULElements(nsGkAtoms::toolbarseparator,
400 nsGkAtoms::toolbarspacer,
401 nsGkAtoms::toolbarspring);
404 ////////////////////////////////////////////////////////////////////////////////
405 // XULToolbarButtonAccessible: Widgets
407 bool XULToolbarButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
408 return XULButtonAccessible::IsAcceptableChild(aEl) ||
409 // In addition to the children allowed by buttons, toolbarbuttons can
410 // have labels as children, but only if the label attribute is not
411 // present.
412 (aEl->IsXULElement(nsGkAtoms::label) &&
413 !mContent->AsElement()->HasAttr(nsGkAtoms::label));
416 ////////////////////////////////////////////////////////////////////////////////
417 // XULToolbarAccessible
418 ////////////////////////////////////////////////////////////////////////////////
420 XULToolbarAccessible::XULToolbarAccessible(nsIContent* aContent,
421 DocAccessible* aDoc)
422 : AccessibleWrap(aContent, aDoc) {}
424 role XULToolbarAccessible::NativeRole() const { return roles::TOOLBAR; }
426 ENameValueFlag XULToolbarAccessible::NativeName(nsString& aName) const {
427 if (mContent->AsElement()->GetAttr(nsGkAtoms::toolbarname, aName)) {
428 aName.CompressWhitespace();
431 return eNameOK;
434 ////////////////////////////////////////////////////////////////////////////////
435 // XULToolbarAccessible
436 ////////////////////////////////////////////////////////////////////////////////
438 XULToolbarSeparatorAccessible::XULToolbarSeparatorAccessible(
439 nsIContent* aContent, DocAccessible* aDoc)
440 : LeafAccessible(aContent, aDoc) {}
442 role XULToolbarSeparatorAccessible::NativeRole() const {
443 return roles::SEPARATOR;
446 uint64_t XULToolbarSeparatorAccessible::NativeState() const { return 0; }