diff options
Diffstat (limited to 'accessible')
76 files changed, 2269 insertions, 463 deletions
diff --git a/accessible/android/AccessibleWrap.cpp b/accessible/android/AccessibleWrap.cpp index 4ca06dea88..5b69866c05 100644 --- a/accessible/android/AccessibleWrap.cpp +++ b/accessible/android/AccessibleWrap.cpp @@ -356,7 +356,8 @@ void AccessibleWrap::SetVirtualViewID(Accessible* aAccessible, int32_t AccessibleWrap::GetAndroidClass(role aRole) { #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::geckoRole: \ return androidClass; diff --git a/accessible/android/Platform.cpp b/accessible/android/Platform.cpp index 312dbc9636..788dc44284 100644 --- a/accessible/android/Platform.cpp +++ b/accessible/android/Platform.cpp @@ -65,7 +65,8 @@ void a11y::PlatformInit() { // Preload any roles that have localized versions #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ rv = stringBundle->GetStringFromName(stringRole, localizedStr); \ if (NS_SUCCEEDED(rv)) { \ sLocalizedStrings.InsertOrUpdate(u##stringRole##_ns, localizedStr); \ diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp index 522434561f..0f17fc59ee 100644 --- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -582,7 +582,8 @@ AtkRole getRoleCB(AtkObject* aAtkObj) { #endif #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::geckoRole: \ aAtkObj->role = atkRole; \ break; diff --git a/accessible/base/ARIAMap.cpp b/accessible/base/ARIAMap.cpp index bfc41db82e..d53592acf0 100644 --- a/accessible/base/ARIAMap.cpp +++ b/accessible/base/ARIAMap.cpp @@ -1094,7 +1094,7 @@ static const nsRoleMapEntry sWAIRoleMaps[] = { }, { // rowgroup nsGkAtoms::rowgroup, - roles::GROUPING, + roles::ROWGROUP, kUseMapRole, eNoValue, eNoAction, diff --git a/accessible/base/AccGroupInfo.cpp b/accessible/base/AccGroupInfo.cpp index c3501dc36e..336ce9ecf5 100644 --- a/accessible/base/AccGroupInfo.cpp +++ b/accessible/base/AccGroupInfo.cpp @@ -292,6 +292,7 @@ uint32_t AccGroupInfo::TotalItemCount(Accessible* aContainer, case roles::MENUPOPUP: case roles::COMBOBOX: case roles::GROUPING: + case roles::ROWGROUP: case roles::TREE_TABLE: case roles::COMBOBOX_LIST: case roles::LISTBOX: diff --git a/accessible/base/Asserts.cpp b/accessible/base/Asserts.cpp index 729b4e1ea5..454eaffef6 100644 --- a/accessible/base/Asserts.cpp +++ b/accessible/base/Asserts.cpp @@ -12,7 +12,8 @@ using namespace mozilla::a11y; #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ static_assert( \ static_cast<uint32_t>(roles::geckoRole) == \ static_cast<uint32_t>(nsIAccessibleRole::ROLE_##geckoRole), \ diff --git a/accessible/base/CachedTableAccessible.cpp b/accessible/base/CachedTableAccessible.cpp index e780bd2a89..8f56352acd 100644 --- a/accessible/base/CachedTableAccessible.cpp +++ b/accessible/base/CachedTableAccessible.cpp @@ -35,7 +35,7 @@ class TablePartRule : public PivotRule { accRole == roles::TEXT || accRole == roles::TEXT_CONTAINER || accRole == roles::SECTION || // Row groups. - accRole == roles::GROUPING) { + accRole == roles::ROWGROUP) { // Walk inside these, but don't match them. return nsIAccessibleTraversalRule::FILTER_IGNORE; } diff --git a/accessible/base/DocManager.cpp b/accessible/base/DocManager.cpp index b7a5203e40..18bf9c0433 100644 --- a/accessible/base/DocManager.cpp +++ b/accessible/base/DocManager.cpp @@ -17,6 +17,7 @@ # include "Logging.h" #endif +#include "mozilla/BasePrincipal.h" #include "mozilla/Components.h" #include "mozilla/EventListenerManager.h" #include "mozilla/PresShell.h" @@ -464,7 +465,18 @@ DocAccessible* DocManager::CreateDocOrRootAccessible(Document* aDocument) { // XXXaaronl: ideally we would traverse the presshell chain. Since there's // no easy way to do that, we cheat and use the document hierarchy. parentDocAcc = GetDocAccessible(aDocument->GetInProcessParentDocument()); - NS_ASSERTION(parentDocAcc, "Can't create an accessible for the document!"); + // We should always get parentDocAcc except sometimes for background + // extension pages, where the parent has an invisible DocShell but the child + // does not. See bug 1888649. + NS_ASSERTION( + parentDocAcc || + (BasePrincipal::Cast(aDocument->GetPrincipal())->AddonPolicy() && + aDocument->GetInProcessParentDocument() && + aDocument->GetInProcessParentDocument()->GetDocShell() && + aDocument->GetInProcessParentDocument() + ->GetDocShell() + ->IsInvisible()), + "Can't create an accessible for the document!"); if (!parentDocAcc) return nullptr; } diff --git a/accessible/base/HTMLMarkupMap.h b/accessible/base/HTMLMarkupMap.h index c607616518..3b422ac09c 100644 --- a/accessible/base/HTMLMarkupMap.h +++ b/accessible/base/HTMLMarkupMap.h @@ -376,7 +376,7 @@ MARKUPMAP( MARKUPMAP(time, New_HyperText, roles::TIME, Attr(xmlroles, time), AttrFromDOM(datetime, datetime)) -MARKUPMAP(tbody, nullptr, roles::GROUPING) +MARKUPMAP(tbody, nullptr, roles::ROWGROUP) MARKUPMAP( td, @@ -392,7 +392,7 @@ MARKUPMAP( }, 0) -MARKUPMAP(tfoot, nullptr, roles::GROUPING) +MARKUPMAP(tfoot, nullptr, roles::ROWGROUP) MARKUPMAP( th, @@ -404,7 +404,7 @@ MARKUPMAP( }, 0) -MARKUPMAP(thead, nullptr, roles::GROUPING) +MARKUPMAP(thead, nullptr, roles::ROWGROUP) MARKUPMAP( tr, diff --git a/accessible/base/RoleMap.h b/accessible/base/RoleMap.h index 58cbb82165..6008dc4bbf 100644 --- a/accessible/base/RoleMap.h +++ b/accessible/base/RoleMap.h @@ -5,7 +5,7 @@ // clang-format off /** * Usage: declare the macro ROLE()with the following arguments: - * ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, msaaRole, ia2Role, nameRule) + * ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, msaaRole, ia2Role, iosIsElement, uiaControlType, nameRule) */ ROLE(NOTHING, @@ -18,6 +18,7 @@ ROLE(NOTHING, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::No, + UIA_CustomControlTypeId, eNameFromSubtreeIfReqRule) ROLE(MENUBAR, @@ -30,6 +31,7 @@ ROLE(MENUBAR, ROLE_SYSTEM_MENUBAR, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_MenuBarControlTypeId, eNoNameRule) ROLE(SCROLLBAR, @@ -42,6 +44,7 @@ ROLE(SCROLLBAR, ROLE_SYSTEM_SCROLLBAR, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ScrollBarControlTypeId, eNameFromValueRule) ROLE(ALERT, @@ -54,6 +57,7 @@ ROLE(ALERT, ROLE_SYSTEM_ALERT, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) ROLE(INTERNAL_FRAME, @@ -66,6 +70,7 @@ ROLE(INTERNAL_FRAME, IA2_ROLE_INTERNAL_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_PaneControlTypeId, eNoNameRule) ROLE(MENUPOPUP, @@ -78,6 +83,7 @@ ROLE(MENUPOPUP, ROLE_SYSTEM_MENUPOPUP, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_MenuControlTypeId, eNoNameRule) ROLE(MENUITEM, @@ -90,6 +96,7 @@ ROLE(MENUITEM, ROLE_SYSTEM_MENUITEM, java::SessionAccessibility::CLASSNAME_MENUITEM, IsAccessibilityElementRule::Yes, + UIA_MenuItemControlTypeId, eNameFromSubtreeRule) ROLE(TOOLTIP, @@ -102,6 +109,7 @@ ROLE(TOOLTIP, ROLE_SYSTEM_TOOLTIP, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::Yes, + UIA_ToolTipControlTypeId, eNameFromSubtreeRule) ROLE(APPLICATION, @@ -114,6 +122,7 @@ ROLE(APPLICATION, ROLE_SYSTEM_APPLICATION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_PaneControlTypeId, eNoNameRule) ROLE(DOCUMENT, @@ -126,6 +135,7 @@ ROLE(DOCUMENT, ROLE_SYSTEM_DOCUMENT, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_DocumentControlTypeId, eNoNameRule) /** @@ -147,6 +157,7 @@ ROLE(PANE, ROLE_SYSTEM_GROUPING, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_PaneControlTypeId, eNoNameRule) ROLE(DIALOG, @@ -159,6 +170,7 @@ ROLE(DIALOG, ROLE_SYSTEM_DIALOG, java::SessionAccessibility::CLASSNAME_DIALOG, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_PaneControlTypeId, eNoNameRule) ROLE(GROUPING, @@ -171,6 +183,7 @@ ROLE(GROUPING, ROLE_SYSTEM_GROUPING, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) ROLE(SEPARATOR, @@ -183,6 +196,7 @@ ROLE(SEPARATOR, ROLE_SYSTEM_SEPARATOR, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::Yes, + UIA_SeparatorControlTypeId, eNoNameRule) ROLE(TOOLBAR, @@ -195,6 +209,7 @@ ROLE(TOOLBAR, ROLE_SYSTEM_TOOLBAR, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ToolBarControlTypeId, eNoNameRule) ROLE(STATUSBAR, @@ -207,6 +222,7 @@ ROLE(STATUSBAR, ROLE_SYSTEM_STATUSBAR, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(TABLE, @@ -219,6 +235,7 @@ ROLE(TABLE, ROLE_SYSTEM_TABLE, java::SessionAccessibility::CLASSNAME_GRIDVIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TableControlTypeId, eNameFromSubtreeIfReqRule) ROLE(COLUMNHEADER, @@ -231,6 +248,7 @@ ROLE(COLUMNHEADER, ROLE_SYSTEM_COLUMNHEADER, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_DataItemControlTypeId, eNameFromSubtreeRule) ROLE(ROWHEADER, @@ -243,6 +261,7 @@ ROLE(ROWHEADER, ROLE_SYSTEM_ROWHEADER, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_HeaderItemControlTypeId, eNameFromSubtreeRule) ROLE(ROW, @@ -255,6 +274,7 @@ ROLE(ROW, ROLE_SYSTEM_ROW, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_DataItemControlTypeId, eNameFromSubtreeRule) ROLE(CELL, @@ -267,6 +287,7 @@ ROLE(CELL, ROLE_SYSTEM_CELL, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_DataItemControlTypeId, eNameFromSubtreeIfReqRule) ROLE(LINK, @@ -279,6 +300,7 @@ ROLE(LINK, ROLE_SYSTEM_LINK, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfBrokenUp, + UIA_HyperlinkControlTypeId, eNameFromSubtreeRule) ROLE(LIST, @@ -291,6 +313,7 @@ ROLE(LIST, ROLE_SYSTEM_LIST, java::SessionAccessibility::CLASSNAME_LISTVIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ListControlTypeId, eNameFromSubtreeIfReqRule) ROLE(LISTITEM, @@ -303,6 +326,7 @@ ROLE(LISTITEM, ROLE_SYSTEM_LISTITEM, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ListItemControlTypeId, eNameFromSubtreeRule) ROLE(OUTLINE, @@ -315,6 +339,7 @@ ROLE(OUTLINE, ROLE_SYSTEM_OUTLINE, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TreeControlTypeId, eNoNameRule) ROLE(OUTLINEITEM, @@ -327,6 +352,7 @@ ROLE(OUTLINEITEM, ROLE_SYSTEM_OUTLINEITEM, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TreeItemControlTypeId, eNameFromSubtreeRule) ROLE(PAGETAB, @@ -339,6 +365,7 @@ ROLE(PAGETAB, ROLE_SYSTEM_PAGETAB, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::Yes, + UIA_TabItemControlTypeId, eNameFromSubtreeRule) ROLE(PROPERTYPAGE, @@ -351,6 +378,7 @@ ROLE(PROPERTYPAGE, ROLE_SYSTEM_PROPERTYPAGE, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_PaneControlTypeId, eNoNameRule) ROLE(GRAPHIC, @@ -363,6 +391,7 @@ ROLE(GRAPHIC, ROLE_SYSTEM_GRAPHIC, java::SessionAccessibility::CLASSNAME_IMAGE, IsAccessibilityElementRule::Yes, + UIA_ImageControlTypeId, eNoNameRule) ROLE(STATICTEXT, @@ -375,6 +404,7 @@ ROLE(STATICTEXT, ROLE_SYSTEM_STATICTEXT, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfParentIsntElementWithName, + UIA_TextControlTypeId, eNoNameRule) ROLE(TEXT_LEAF, @@ -387,6 +417,7 @@ ROLE(TEXT_LEAF, ROLE_SYSTEM_TEXT, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfParentIsntElementWithName, + UIA_TextControlTypeId, eNoNameRule) ROLE(PUSHBUTTON, @@ -399,6 +430,7 @@ ROLE(PUSHBUTTON, ROLE_SYSTEM_PUSHBUTTON, java::SessionAccessibility::CLASSNAME_BUTTON, IsAccessibilityElementRule::Yes, + UIA_ButtonControlTypeId, eNameFromSubtreeRule) ROLE(CHECKBUTTON, @@ -411,6 +443,7 @@ ROLE(CHECKBUTTON, ROLE_SYSTEM_CHECKBUTTON, java::SessionAccessibility::CLASSNAME_CHECKBOX, IsAccessibilityElementRule::Yes, + UIA_CheckBoxControlTypeId, eNameFromSubtreeRule) ROLE(RADIOBUTTON, @@ -423,6 +456,7 @@ ROLE(RADIOBUTTON, ROLE_SYSTEM_RADIOBUTTON, java::SessionAccessibility::CLASSNAME_RADIOBUTTON, IsAccessibilityElementRule::Yes, + UIA_RadioButtonControlTypeId, eNameFromSubtreeRule) // Equivalent of HTML select element with size="1". See also EDITCOMBOBOX. @@ -436,6 +470,7 @@ ROLE(COMBOBOX, ROLE_SYSTEM_COMBOBOX, java::SessionAccessibility::CLASSNAME_SPINNER, IsAccessibilityElementRule::Yes, + UIA_ComboBoxControlTypeId, eNameFromValueRule) ROLE(PROGRESSBAR, @@ -448,6 +483,7 @@ ROLE(PROGRESSBAR, ROLE_SYSTEM_PROGRESSBAR, java::SessionAccessibility::CLASSNAME_PROGRESSBAR, IsAccessibilityElementRule::Yes, + UIA_ProgressBarControlTypeId, eNameFromValueRule) ROLE(SLIDER, @@ -460,6 +496,7 @@ ROLE(SLIDER, ROLE_SYSTEM_SLIDER, java::SessionAccessibility::CLASSNAME_SEEKBAR, IsAccessibilityElementRule::Yes, + UIA_SliderControlTypeId, eNameFromValueRule) ROLE(SPINBUTTON, @@ -472,6 +509,7 @@ ROLE(SPINBUTTON, ROLE_SYSTEM_SPINBUTTON, java::SessionAccessibility::CLASSNAME_EDITTEXT, IsAccessibilityElementRule::Yes, + UIA_SpinnerControlTypeId, eNameFromValueRule) ROLE(DIAGRAM, @@ -484,6 +522,7 @@ ROLE(DIAGRAM, ROLE_SYSTEM_DIAGRAM, java::SessionAccessibility::CLASSNAME_IMAGE, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ImageControlTypeId, eNoNameRule) ROLE(ANIMATION, @@ -496,6 +535,7 @@ ROLE(ANIMATION, ROLE_SYSTEM_ANIMATION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(BUTTONDROPDOWN, @@ -508,6 +548,7 @@ ROLE(BUTTONDROPDOWN, ROLE_SYSTEM_BUTTONDROPDOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::Yes, + UIA_SplitButtonControlTypeId, eNameFromSubtreeRule) ROLE(BUTTONMENU, @@ -520,6 +561,7 @@ ROLE(BUTTONMENU, ROLE_SYSTEM_BUTTONMENU, java::SessionAccessibility::CLASSNAME_SPINNER, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ButtonControlTypeId, eNameFromSubtreeRule) ROLE(WHITESPACE, @@ -532,6 +574,7 @@ ROLE(WHITESPACE, ROLE_SYSTEM_WHITESPACE, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::No, + UIA_TextControlTypeId, eNoNameRule) ROLE(PAGETABLIST, @@ -544,6 +587,7 @@ ROLE(PAGETABLIST, ROLE_SYSTEM_PAGETABLIST, java::SessionAccessibility::CLASSNAME_TABWIDGET, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TabControlTypeId, eNoNameRule) ROLE(CANVAS, @@ -556,6 +600,7 @@ ROLE(CANVAS, IA2_ROLE_CANVAS, java::SessionAccessibility::CLASSNAME_IMAGE, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ImageControlTypeId, eNoNameRule) ROLE(CHECK_MENU_ITEM, @@ -568,6 +613,7 @@ ROLE(CHECK_MENU_ITEM, IA2_ROLE_CHECK_MENU_ITEM, java::SessionAccessibility::CLASSNAME_MENUITEM, IsAccessibilityElementRule::Yes, + UIA_MenuItemControlTypeId, eNameFromSubtreeRule) ROLE(DATE_EDITOR, @@ -580,6 +626,7 @@ ROLE(DATE_EDITOR, IA2_ROLE_DATE_EDITOR, java::SessionAccessibility::CLASSNAME_SPINNER, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(CHROME_WINDOW, @@ -592,6 +639,7 @@ ROLE(CHROME_WINDOW, IA2_ROLE_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_WindowControlTypeId, eNoNameRule) ROLE(LABEL, @@ -604,6 +652,7 @@ ROLE(LABEL, IA2_ROLE_LABEL, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeRule) ROLE(PASSWORD_TEXT, @@ -616,6 +665,7 @@ ROLE(PASSWORD_TEXT, ROLE_SYSTEM_TEXT, java::SessionAccessibility::CLASSNAME_EDITTEXT, IsAccessibilityElementRule::Yes, + UIA_EditControlTypeId, eNoNameRule) ROLE(RADIO_MENU_ITEM, @@ -628,6 +678,7 @@ ROLE(RADIO_MENU_ITEM, IA2_ROLE_RADIO_MENU_ITEM, java::SessionAccessibility::CLASSNAME_MENUITEM, IsAccessibilityElementRule::Yes, + UIA_MenuItemControlTypeId, eNameFromSubtreeRule) ROLE(TEXT_CONTAINER, @@ -640,6 +691,7 @@ ROLE(TEXT_CONTAINER, IA2_ROLE_TEXT_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) ROLE(TOGGLE_BUTTON, @@ -652,6 +704,7 @@ ROLE(TOGGLE_BUTTON, IA2_ROLE_TOGGLE_BUTTON, java::SessionAccessibility::CLASSNAME_TOGGLEBUTTON, IsAccessibilityElementRule::Yes, + UIA_ButtonControlTypeId, eNameFromSubtreeRule) ROLE(TREE_TABLE, @@ -664,6 +717,7 @@ ROLE(TREE_TABLE, ROLE_SYSTEM_OUTLINE, java::SessionAccessibility::CLASSNAME_GRIDVIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_DataGridControlTypeId, eNoNameRule) ROLE(PARAGRAPH, @@ -676,6 +730,7 @@ ROLE(PARAGRAPH, IA2_ROLE_PARAGRAPH, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(ENTRY, @@ -688,6 +743,7 @@ ROLE(ENTRY, ROLE_SYSTEM_TEXT, java::SessionAccessibility::CLASSNAME_EDITTEXT, IsAccessibilityElementRule::Yes, + UIA_EditControlTypeId, eNameFromValueRule) ROLE(CAPTION, @@ -700,6 +756,7 @@ ROLE(CAPTION, IA2_ROLE_CAPTION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(NON_NATIVE_DOCUMENT, @@ -712,6 +769,7 @@ ROLE(NON_NATIVE_DOCUMENT, ROLE_SYSTEM_DOCUMENT, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_DocumentControlTypeId, eNoNameRule) ROLE(HEADING, @@ -724,6 +782,7 @@ ROLE(HEADING, IA2_ROLE_HEADING, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildless, + UIA_TextControlTypeId, eNameFromSubtreeRule) ROLE(SECTION, @@ -736,6 +795,7 @@ ROLE(SECTION, IA2_ROLE_SECTION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) ROLE(FORM, @@ -748,6 +808,7 @@ ROLE(FORM, IA2_ROLE_FORM, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(APP_ROOT, @@ -760,6 +821,7 @@ ROLE(APP_ROOT, ROLE_SYSTEM_APPLICATION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_WindowControlTypeId, eNoNameRule) ROLE(PARENT_MENUITEM, @@ -772,6 +834,7 @@ ROLE(PARENT_MENUITEM, ROLE_SYSTEM_MENUITEM, java::SessionAccessibility::CLASSNAME_MENUITEM, IsAccessibilityElementRule::Yes, + UIA_MenuItemControlTypeId, eNameFromSubtreeRule) ROLE(COMBOBOX_LIST, @@ -784,6 +847,7 @@ ROLE(COMBOBOX_LIST, ROLE_SYSTEM_LIST, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::No, + UIA_ListControlTypeId, eNoNameRule) ROLE(COMBOBOX_OPTION, @@ -796,6 +860,7 @@ ROLE(COMBOBOX_OPTION, ROLE_SYSTEM_LISTITEM, java::SessionAccessibility::CLASSNAME_MENUITEM, IsAccessibilityElementRule::Yes, + UIA_ListItemControlTypeId, eNameFromSubtreeRule) ROLE(IMAGE_MAP, @@ -808,6 +873,7 @@ ROLE(IMAGE_MAP, ROLE_SYSTEM_GRAPHIC, java::SessionAccessibility::CLASSNAME_IMAGE, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ImageControlTypeId, eNoNameRule) ROLE(OPTION, @@ -820,6 +886,7 @@ ROLE(OPTION, ROLE_SYSTEM_LISTITEM, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::Yes, + UIA_ListItemControlTypeId, eNameFromSubtreeRule) ROLE(RICH_OPTION, @@ -832,6 +899,7 @@ ROLE(RICH_OPTION, ROLE_SYSTEM_LISTITEM, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ListItemControlTypeId, eNameFromSubtreeRule) ROLE(LISTBOX, @@ -844,6 +912,7 @@ ROLE(LISTBOX, ROLE_SYSTEM_LIST, java::SessionAccessibility::CLASSNAME_LISTVIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ListControlTypeId, eNameFromValueRule) ROLE(FLAT_EQUATION, @@ -856,6 +925,7 @@ ROLE(FLAT_EQUATION, ROLE_SYSTEM_EQUATION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::Yes, + UIA_GroupControlTypeId, eNoNameRule) ROLE(GRID_CELL, @@ -868,6 +938,7 @@ ROLE(GRID_CELL, ROLE_SYSTEM_CELL, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_DataItemControlTypeId, eNameFromSubtreeRule) ROLE(NOTE, @@ -880,6 +951,7 @@ ROLE(NOTE, IA2_ROLE_NOTE, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) ROLE(FIGURE, @@ -892,6 +964,7 @@ ROLE(FIGURE, ROLE_SYSTEM_GROUPING, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(CHECK_RICH_OPTION, @@ -904,6 +977,7 @@ ROLE(CHECK_RICH_OPTION, ROLE_SYSTEM_CHECKBUTTON, java::SessionAccessibility::CLASSNAME_CHECKBOX, IsAccessibilityElementRule::Yes, + UIA_CheckBoxControlTypeId, eNameFromSubtreeRule) ROLE(DEFINITION_LIST, @@ -916,6 +990,7 @@ ROLE(DEFINITION_LIST, ROLE_SYSTEM_LIST, java::SessionAccessibility::CLASSNAME_LISTVIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ListControlTypeId, eNameFromSubtreeIfReqRule) ROLE(TERM, @@ -928,6 +1003,7 @@ ROLE(TERM, ROLE_SYSTEM_LISTITEM, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeRule) ROLE(DEFINITION, @@ -940,6 +1016,7 @@ ROLE(DEFINITION, IA2_ROLE_PARAGRAPH, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeRule) ROLE(KEY, @@ -952,6 +1029,7 @@ ROLE(KEY, ROLE_SYSTEM_PUSHBUTTON, java::SessionAccessibility::CLASSNAME_BUTTON, IsAccessibilityElementRule::Yes, + UIA_ButtonControlTypeId, eNameFromSubtreeRule) ROLE(SWITCH, @@ -964,6 +1042,7 @@ ROLE(SWITCH, IA2_ROLE_TOGGLE_BUTTON, java::SessionAccessibility::CLASSNAME_CHECKBOX, IsAccessibilityElementRule::Yes, + UIA_ButtonControlTypeId, eNameFromSubtreeRule) ROLE(MATHML_MATH, @@ -976,6 +1055,7 @@ ROLE(MATHML_MATH, ROLE_SYSTEM_EQUATION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_IDENTIFIER, @@ -988,6 +1068,7 @@ ROLE(MATHML_IDENTIFIER, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeRule) ROLE(MATHML_NUMBER, @@ -1000,6 +1081,7 @@ ROLE(MATHML_NUMBER, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeRule) ROLE(MATHML_OPERATOR, @@ -1017,6 +1099,7 @@ ROLE(MATHML_OPERATOR, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeRule) ROLE(MATHML_TEXT, @@ -1029,6 +1112,7 @@ ROLE(MATHML_TEXT, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeRule) ROLE(MATHML_STRING_LITERAL, @@ -1041,6 +1125,7 @@ ROLE(MATHML_STRING_LITERAL, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeRule) ROLE(MATHML_GLYPH, @@ -1053,6 +1138,7 @@ ROLE(MATHML_GLYPH, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_IMAGE, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeRule) ROLE(MATHML_ROW, @@ -1065,6 +1151,7 @@ ROLE(MATHML_ROW, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_FRACTION, @@ -1077,6 +1164,7 @@ ROLE(MATHML_FRACTION, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_SQUARE_ROOT, @@ -1089,6 +1177,7 @@ ROLE(MATHML_SQUARE_ROOT, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_ROOT, @@ -1101,6 +1190,7 @@ ROLE(MATHML_ROOT, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_ENCLOSED, @@ -1113,6 +1203,7 @@ ROLE(MATHML_ENCLOSED, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_STYLE, @@ -1125,6 +1216,7 @@ ROLE(MATHML_STYLE, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_SUB, @@ -1137,6 +1229,7 @@ ROLE(MATHML_SUB, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_SUP, @@ -1149,6 +1242,7 @@ ROLE(MATHML_SUP, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_SUB_SUP, @@ -1161,6 +1255,7 @@ ROLE(MATHML_SUB_SUP, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_UNDER, @@ -1173,6 +1268,7 @@ ROLE(MATHML_UNDER, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_OVER, @@ -1185,6 +1281,7 @@ ROLE(MATHML_OVER, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_UNDER_OVER, @@ -1197,6 +1294,7 @@ ROLE(MATHML_UNDER_OVER, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_MULTISCRIPTS, @@ -1209,6 +1307,7 @@ ROLE(MATHML_MULTISCRIPTS, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_TABLE, @@ -1221,6 +1320,7 @@ ROLE(MATHML_TABLE, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_GRIDVIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_LABELED_ROW, @@ -1233,6 +1333,7 @@ ROLE(MATHML_LABELED_ROW, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_TABLE_ROW, @@ -1245,6 +1346,7 @@ ROLE(MATHML_TABLE_ROW, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_CELL, @@ -1257,6 +1359,7 @@ ROLE(MATHML_CELL, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_ACTION, @@ -1269,6 +1372,7 @@ ROLE(MATHML_ACTION, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_ERROR, @@ -1281,6 +1385,7 @@ ROLE(MATHML_ERROR, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_STACK, @@ -1293,6 +1398,7 @@ ROLE(MATHML_STACK, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_LONG_DIVISION, @@ -1305,6 +1411,7 @@ ROLE(MATHML_LONG_DIVISION, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_STACK_GROUP, @@ -1317,6 +1424,7 @@ ROLE(MATHML_STACK_GROUP, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_STACK_ROW, @@ -1329,6 +1437,7 @@ ROLE(MATHML_STACK_ROW, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_STACK_CARRIES, @@ -1341,6 +1450,7 @@ ROLE(MATHML_STACK_CARRIES, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_STACK_CARRY, @@ -1353,6 +1463,7 @@ ROLE(MATHML_STACK_CARRY, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MATHML_STACK_LINE, @@ -1365,6 +1476,7 @@ ROLE(MATHML_STACK_LINE, IA2_ROLE_UNKNOWN, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(RADIO_GROUP, @@ -1377,6 +1489,7 @@ ROLE(RADIO_GROUP, ROLE_SYSTEM_GROUPING, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_ListControlTypeId, eNoNameRule) ROLE(TEXT, @@ -1389,6 +1502,7 @@ ROLE(TEXT, IA2_ROLE_TEXT_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) ROLE(DETAILS, @@ -1401,6 +1515,7 @@ ROLE(DETAILS, ROLE_SYSTEM_GROUPING, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(SUMMARY, @@ -1413,6 +1528,7 @@ ROLE(SUMMARY, ROLE_SYSTEM_PUSHBUTTON, java::SessionAccessibility::CLASSNAME_BUTTON, IsAccessibilityElementRule::Yes, + UIA_ButtonControlTypeId, eNameFromSubtreeRule) ROLE(LANDMARK, @@ -1425,6 +1541,7 @@ ROLE(LANDMARK, IA2_ROLE_LANDMARK, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(NAVIGATION, @@ -1437,6 +1554,7 @@ ROLE(NAVIGATION, IA2_ROLE_LANDMARK, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(FOOTNOTE, @@ -1449,6 +1567,7 @@ ROLE(FOOTNOTE, IA2_ROLE_FOOTNOTE, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(ARTICLE, @@ -1461,6 +1580,7 @@ ROLE(ARTICLE, ROLE_SYSTEM_DOCUMENT, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(REGION, @@ -1473,6 +1593,7 @@ ROLE(REGION, IA2_ROLE_LANDMARK, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) // A composite widget with a text input and popup. Used for ARIA role combobox. @@ -1487,6 +1608,7 @@ ROLE(EDITCOMBOBOX, ROLE_SYSTEM_COMBOBOX, java::SessionAccessibility::CLASSNAME_EDITTEXT, IsAccessibilityElementRule::Yes, + UIA_ComboBoxControlTypeId, eNameFromValueRule) ROLE(BLOCKQUOTE, @@ -1499,6 +1621,7 @@ ROLE(BLOCKQUOTE, IA2_ROLE_BLOCK_QUOTE, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(CONTENT_DELETION, @@ -1511,6 +1634,7 @@ ROLE(CONTENT_DELETION, IA2_ROLE_CONTENT_DELETION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(CONTENT_INSERTION, @@ -1523,6 +1647,7 @@ ROLE(CONTENT_INSERTION, IA2_ROLE_CONTENT_INSERTION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(FORM_LANDMARK, @@ -1535,6 +1660,7 @@ ROLE(FORM_LANDMARK, IA2_ROLE_FORM, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(MARK, @@ -1547,6 +1673,7 @@ ROLE(MARK, IA2_ROLE_MARK, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) ROLE(SUGGESTION, @@ -1559,6 +1686,7 @@ ROLE(SUGGESTION, IA2_ROLE_SUGGESTION, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(COMMENT, @@ -1571,6 +1699,7 @@ ROLE(COMMENT, IA2_ROLE_COMMENT, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNoNameRule) ROLE(CODE, @@ -1583,6 +1712,7 @@ ROLE(CODE, IA2_ROLE_TEXT_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(TIME_EDITOR, @@ -1595,6 +1725,7 @@ ROLE(TIME_EDITOR, ROLE_SYSTEM_GROUPING, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) ROLE(LISTITEM_MARKER, @@ -1607,6 +1738,7 @@ ROLE(LISTITEM_MARKER, ROLE_SYSTEM_STATICTEXT, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNoNameRule) ROLE(METER, @@ -1619,6 +1751,7 @@ ROLE(METER, ROLE_SYSTEM_PROGRESSBAR, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::Yes, + UIA_ProgressBarControlTypeId, eNameFromValueRule) ROLE(SUBSCRIPT, @@ -1631,6 +1764,7 @@ ROLE(SUBSCRIPT, IA2_ROLE_TEXT_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(SUPERSCRIPT, @@ -1643,6 +1777,7 @@ ROLE(SUPERSCRIPT, IA2_ROLE_TEXT_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(EMPHASIS, @@ -1655,6 +1790,7 @@ ROLE(EMPHASIS, IA2_ROLE_TEXT_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(STRONG, @@ -1667,6 +1803,7 @@ ROLE(STRONG, IA2_ROLE_TEXT_FRAME, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(TIME, @@ -1679,6 +1816,7 @@ ROLE(TIME, ROLE_SYSTEM_GROUPING, java::SessionAccessibility::CLASSNAME_VIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_TextControlTypeId, eNameFromSubtreeIfReqRule) ROLE(GRID, @@ -1691,5 +1829,19 @@ ROLE(GRID, ROLE_SYSTEM_TABLE, java::SessionAccessibility::CLASSNAME_GRIDVIEW, IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_DataGridControlTypeId, + eNameFromSubtreeIfReqRule) + +ROLE(ROWGROUP, + "rowgroup", + nsGkAtoms::rowgroup, + ATK_ROLE_PANEL, + NSAccessibilityGroupRole, + NSAccessibilityUnknownSubrole, + ROLE_SYSTEM_GROUPING, + ROLE_SYSTEM_GROUPING, + java::SessionAccessibility::CLASSNAME_VIEW, + IsAccessibilityElementRule::IfChildlessWithNameAndFocusable, + UIA_GroupControlTypeId, eNameFromSubtreeIfReqRule) // clang-format on diff --git a/accessible/base/nsAccUtils.cpp b/accessible/base/nsAccUtils.cpp index 82af56348f..61211068ca 100644 --- a/accessible/base/nsAccUtils.cpp +++ b/accessible/base/nsAccUtils.cpp @@ -583,8 +583,9 @@ const nsAttrValue* nsAccUtils::GetARIAAttr(dom::Element* aElement, bool nsAccUtils::ARIAAttrValueIs(dom::Element* aElement, const nsAtom* aName, const nsAString& aValue, nsCaseTreatment aCaseSensitive) { - if (aElement->AttrValueIs(kNameSpaceID_None, aName, aValue, aCaseSensitive)) { - return true; + if (aElement->HasAttr(kNameSpaceID_None, aName)) { + return aElement->AttrValueIs(kNameSpaceID_None, aName, aValue, + aCaseSensitive); } const auto* defaults = GetARIADefaults(aElement); if (!defaults) { @@ -597,8 +598,9 @@ bool nsAccUtils::ARIAAttrValueIs(dom::Element* aElement, const nsAtom* aName, bool nsAccUtils::ARIAAttrValueIs(dom::Element* aElement, const nsAtom* aName, const nsAtom* aValue, nsCaseTreatment aCaseSensitive) { - if (aElement->AttrValueIs(kNameSpaceID_None, aName, aValue, aCaseSensitive)) { - return true; + if (aElement->HasAttr(kNameSpaceID_None, aName)) { + return aElement->AttrValueIs(kNameSpaceID_None, aName, aValue, + aCaseSensitive); } const auto* defaults = GetARIADefaults(aElement); if (!defaults) { diff --git a/accessible/base/nsAccessibilityService.cpp b/accessible/base/nsAccessibilityService.cpp index d38276572b..615af596a7 100644 --- a/accessible/base/nsAccessibilityService.cpp +++ b/accessible/base/nsAccessibilityService.cpp @@ -122,7 +122,7 @@ static LocalAccessible* MaybeCreateSpecificARIAAccessible( if (!parent) { return nullptr; } - if (!parent->IsTable() && parent->Role() == roles::GROUPING) { + if (!parent->IsTable() && parent->Role() == roles::ROWGROUP) { parent = parent->GetNonGenericParent(); if (!parent) { return nullptr; @@ -838,7 +838,8 @@ void nsAccessibilityService::RecreateAccessible(PresShell* aPresShell, void nsAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString) { #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::geckoRole: \ aString.AssignLiteral(stringRole); \ return; diff --git a/accessible/base/nsTextEquivUtils.cpp b/accessible/base/nsTextEquivUtils.cpp index f222930981..769559a2c9 100644 --- a/accessible/base/nsTextEquivUtils.cpp +++ b/accessible/base/nsTextEquivUtils.cpp @@ -165,6 +165,7 @@ nsresult nsTextEquivUtils::AppendFromAccessible(Accessible* aAccessible, // together in final name. const nsStyleDisplay* display = frame->StyleDisplay(); if (display->IsBlockOutsideStyle() || + display->mDisplay == StyleDisplay::InlineBlock || display->mDisplay == StyleDisplay::TableCell) { isHTMLBlock = true; if (!aString->IsEmpty()) { @@ -323,7 +324,8 @@ bool nsTextEquivUtils::AppendString(nsAString* aString, uint32_t nsTextEquivUtils::GetRoleRule(role aRole) { #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::geckoRole: \ return nameRule; diff --git a/accessible/basetypes/Accessible.cpp b/accessible/basetypes/Accessible.cpp index de9740dd18..8b433a0ddd 100644 --- a/accessible/basetypes/Accessible.cpp +++ b/accessible/basetypes/Accessible.cpp @@ -569,8 +569,7 @@ nsStaticAtom* Accessible::ComputedARIARole() const { roleMap->roleAtom != nsGkAtoms::form && (roleMap->roleRule == kUseNativeRole || roleMap->IsOfType(eLandmark) || roleMap->roleAtom == nsGkAtoms::alertdialog || - roleMap->roleAtom == nsGkAtoms::feed || - roleMap->roleAtom == nsGkAtoms::rowgroup)) { + roleMap->roleAtom == nsGkAtoms::feed)) { // Explicit ARIA role (e.g. specified via the role attribute) which does not // map to a unique Gecko role. return roleMap->roleAtom; @@ -583,18 +582,10 @@ nsStaticAtom* Accessible::ComputedARIARole() const { // Landmark role from native markup; e.g. <main>, <nav>. return LandmarkRole(); } - if (geckoRole == roles::GROUPING) { - // Gecko doesn't differentiate between group and rowgroup. It uses - // roles::GROUPING for both. - nsAtom* tag = TagName(); - if (tag == nsGkAtoms::tbody || tag == nsGkAtoms::tfoot || - tag == nsGkAtoms::thead) { - return nsGkAtoms::rowgroup; - } - } // Role from native markup or layout. #define ROLE(_geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::_geckoRole: \ return ariaRole; switch (geckoRole) { diff --git a/accessible/basetypes/Accessible.h b/accessible/basetypes/Accessible.h index 77ac0bb4fc..9068115d01 100644 --- a/accessible/basetypes/Accessible.h +++ b/accessible/basetypes/Accessible.h @@ -185,6 +185,46 @@ class Accessible { */ bool IsBefore(const Accessible* aAcc) const; + /** + * A utility enum for controlling FindAncestorIf. + * Continue: this is not the desired ancestor node, keep searching + * Found: this is the desired ancestor node + * NotFound: this is not the desired ancestor node, stop searching + */ + enum class AncestorSearchOption { Continue, Found, NotFound }; + /** + * Return a non-generic ancestor for which the given predicate returns + * AncestorSearchOption::Found, if any exist. If none exist, return nullptr. + * The predicate may choose to return options from AncestorSearchOption to + * control the flow of the ancestor search. + */ + template <typename Callable> + Accessible* FindAncestorIf(Callable&& aPredicate) const { + static_assert( + std::is_same_v<std::invoke_result_t<Callable, const Accessible&>, + AncestorSearchOption>, + "Given callable must return AncestorSearchOption."); + static_assert(std::is_invocable_v<Callable, const Accessible&>, + "Given callable must accept const Accessible&."); + Accessible* search = GetNonGenericParent(); + while (search) { + const AncestorSearchOption option = aPredicate(*search); + switch (option) { + case AncestorSearchOption::Continue: + search = search->GetNonGenericParent(); + continue; + case AncestorSearchOption::Found: + return search; + case AncestorSearchOption::NotFound: + return nullptr; + default: + MOZ_ASSERT(false, "Unhandled AncestorSearchOption"); + break; + } + } + return nullptr; + } + bool IsAncestorOf(const Accessible* aAcc) const { for (const Accessible* parent = aAcc->Parent(); parent; parent = parent->Parent()) { diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp index a758164e2b..aaf0337a1a 100644 --- a/accessible/generic/LocalAccessible.cpp +++ b/accessible/generic/LocalAccessible.cpp @@ -744,22 +744,8 @@ LayoutDeviceIntRect LocalAccessible::Bounds() const { void LocalAccessible::SetSelected(bool aSelect) { if (!HasOwnContent()) return; - LocalAccessible* select = nsAccUtils::GetSelectableContainer(this, State()); - if (select) { - if (select->State() & states::MULTISELECTABLE) { - if (mContent->IsElement() && ARIARoleMap()) { - if (aSelect) { - mContent->AsElement()->SetAttr( - kNameSpaceID_None, nsGkAtoms::aria_selected, u"true"_ns, true); - } else { - mContent->AsElement()->UnsetAttr(kNameSpaceID_None, - nsGkAtoms::aria_selected, true); - } - } - return; - } - - if (aSelect) TakeFocus(); + if (nsAccUtils::GetSelectableContainer(this, State()) && aSelect) { + TakeFocus(); } } @@ -1819,27 +1805,7 @@ double LocalAccessible::CurValue() const { return checkValue; } -bool LocalAccessible::SetCurValue(double aValue) { - const nsRoleMapEntry* roleMapEntry = ARIARoleMap(); - if (!roleMapEntry || roleMapEntry->valueRule == eNoValue) return false; - - const uint32_t kValueCannotChange = states::READONLY | states::UNAVAILABLE; - if (State() & kValueCannotChange) return false; - - double checkValue = MinValue(); - if (!std::isnan(checkValue) && aValue < checkValue) return false; - - checkValue = MaxValue(); - if (!std::isnan(checkValue) && aValue > checkValue) return false; - - nsAutoString strValue; - strValue.AppendFloat(aValue); - - if (!mContent->IsElement()) return true; - - return NS_SUCCEEDED(mContent->AsElement()->SetAttr( - kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true)); -} +bool LocalAccessible::SetCurValue(double aValue) { return false; } role LocalAccessible::FindNextValidARIARole( std::initializer_list<nsStaticAtom*> aRolesToSkip) const { @@ -1925,6 +1891,16 @@ role LocalAccessible::ARIATransformRole(role aRole) const { return roles::COMBOBOX_OPTION; } + // Orphaned option outside the context of a listbox. + const Accessible* listbox = FindAncestorIf([](const Accessible& aAcc) { + const role accRole = aAcc.Role(); + return accRole == roles::LISTBOX ? AncestorSearchOption::Found + : accRole == roles::GROUPING ? AncestorSearchOption::Continue + : AncestorSearchOption::NotFound; + }); + if (!listbox) { + return NativeRole(); + } } else if (aRole == roles::MENUITEM) { // Menuitem has a submenu. if (mContent->IsElement() && @@ -1934,6 +1910,30 @@ role LocalAccessible::ARIATransformRole(role aRole) const { return roles::PARENT_MENUITEM; } + // Orphaned menuitem outside the context of a menu/menubar. + const Accessible* menu = FindAncestorIf([](const Accessible& aAcc) { + const role accRole = aAcc.Role(); + return (accRole == roles::MENUBAR || accRole == roles::MENUPOPUP) + ? AncestorSearchOption::Found + : accRole == roles::GROUPING ? AncestorSearchOption::Continue + : AncestorSearchOption::NotFound; + }); + if (!menu) { + return NativeRole(); + } + } else if (aRole == roles::RADIO_MENU_ITEM || + aRole == roles::CHECK_MENU_ITEM) { + // Orphaned radio/checkbox menuitem outside the context of a menu/menubar. + const Accessible* menu = FindAncestorIf([](const Accessible& aAcc) { + const role accRole = aAcc.Role(); + return (accRole == roles::MENUBAR || accRole == roles::MENUPOPUP) + ? AncestorSearchOption::Found + : accRole == roles::GROUPING ? AncestorSearchOption::Continue + : AncestorSearchOption::NotFound; + }); + if (!menu) { + return NativeRole(); + } } else if (aRole == roles::CELL) { // A cell inside an ancestor table element that has a grid role needs a // gridcell role @@ -1942,6 +1942,63 @@ role LocalAccessible::ARIATransformRole(role aRole) const { if (table && table->IsARIARole(nsGkAtoms::grid)) { return roles::GRID_CELL; } + } else if (aRole == roles::ROW) { + // Orphaned rows outside the context of a table. + const LocalAccessible* table = nsAccUtils::TableFor(this); + if (!table) { + return NativeRole(); + } + } else if (aRole == roles::ROWGROUP) { + // Orphaned rowgroups outside the context of a table. + const Accessible* table = FindAncestorIf([](const Accessible& aAcc) { + return aAcc.IsTable() ? AncestorSearchOption::Found + : AncestorSearchOption::NotFound; + }); + if (!table) { + return NativeRole(); + } + } else if (aRole == roles::GRID_CELL || aRole == roles::ROWHEADER || + aRole == roles::COLUMNHEADER) { + // Orphaned gridcell/rowheader/columnheader outside the context of a row. + const Accessible* row = FindAncestorIf([](const Accessible& aAcc) { + return aAcc.IsTableRow() ? AncestorSearchOption::Found + : AncestorSearchOption::NotFound; + }); + if (!row) { + return NativeRole(); + } + } else if (aRole == roles::LISTITEM) { + // doc-biblioentry and doc-endnote should not be treated as listitems. + const nsRoleMapEntry* roleMapEntry = ARIARoleMap(); + if (!roleMapEntry || (roleMapEntry->roleAtom != nsGkAtoms::docBiblioentry && + roleMapEntry->roleAtom != nsGkAtoms::docEndnote)) { + // Orphaned listitem outside the context of a list. + const Accessible* list = FindAncestorIf([](const Accessible& aAcc) { + return aAcc.IsList() ? AncestorSearchOption::Found + : AncestorSearchOption::Continue; + }); + if (!list) { + return NativeRole(); + } + } + } else if (aRole == roles::PAGETAB) { + // Orphaned tab outside the context of a tablist. + const Accessible* tablist = FindAncestorIf([](const Accessible& aAcc) { + return aAcc.Role() == roles::PAGETABLIST ? AncestorSearchOption::Found + : AncestorSearchOption::NotFound; + }); + if (!tablist) { + return NativeRole(); + } + } else if (aRole == roles::OUTLINEITEM) { + // Orphaned treeitem outside the context of a tree. + const Accessible* tree = FindAncestorIf([](const Accessible& aAcc) { + return aAcc.Role() == roles::OUTLINE ? AncestorSearchOption::Found + : AncestorSearchOption::Continue; + }); + if (!tree) { + return NativeRole(); + } } return aRole; @@ -2149,12 +2206,14 @@ Relation LocalAccessible::RelationByType(RelationType aType) const { if (roleMapEntry && (roleMapEntry->role == roles::OUTLINEITEM || roleMapEntry->role == roles::LISTITEM || roleMapEntry->role == roles::ROW)) { - Accessible* parent = const_cast<LocalAccessible*>(this) - ->GetOrCreateGroupInfo() - ->ConceptualParent(); - if (parent) { - MOZ_ASSERT(parent->IsLocal()); - rel.AppendTarget(parent->AsLocal()); + AccGroupInfo* groupInfo = + const_cast<LocalAccessible*>(this)->GetOrCreateGroupInfo(); + if (groupInfo) { + Accessible* parent = groupInfo->ConceptualParent(); + if (parent) { + MOZ_ASSERT(parent->IsLocal()); + rel.AppendTarget(parent->AsLocal()); + } } } @@ -2274,7 +2333,7 @@ Relation LocalAccessible::RelationByType(RelationType aType) const { } // If this node is an anchor element, query its hash to find the // target. - nsAutoString hash; + nsAutoCString hash; anchor->GetHash(hash); if (hash.IsEmpty()) { return rel; @@ -2282,11 +2341,11 @@ Relation LocalAccessible::RelationByType(RelationType aType) const { // GetHash returns an ID or name with a leading '#', trim it so we can // search the doc by ID or name alone. - hash.Trim("#"); - if (dom::Element* elm = mContent->OwnerDoc()->GetElementById(hash)) { + NS_ConvertUTF8toUTF16 hash16(Substring(hash, 1)); + if (dom::Element* elm = mContent->OwnerDoc()->GetElementById(hash16)) { rel.AppendTarget(mDoc->GetAccessibleOrContainer(elm)); } else if (nsCOMPtr<nsINodeList> list = - mContent->OwnerDoc()->GetElementsByName(hash)) { + mContent->OwnerDoc()->GetElementsByName(hash16)) { // Loop through the named nodes looking for the first anchor uint32_t length = list->Length(); for (uint32_t i = 0; i < length; i++) { @@ -3069,15 +3128,7 @@ LocalAccessible* LocalAccessible::CurrentItem() const { return nullptr; } -void LocalAccessible::SetCurrentItem(const LocalAccessible* aItem) { - nsAtom* id = aItem->GetContent()->GetID(); - if (id) { - nsAutoString idStr; - id->ToString(idStr); - mContent->AsElement()->SetAttr( - kNameSpaceID_None, nsGkAtoms::aria_activedescendant, idStr, true); - } -} +void LocalAccessible::SetCurrentItem(const LocalAccessible* aItem) {} LocalAccessible* LocalAccessible::ContainerWidget() const { if (HasARIARole() && mContent->HasID()) { diff --git a/accessible/html/HTMLSelectAccessible.cpp b/accessible/html/HTMLSelectAccessible.cpp index f8fb4180c7..02d449df36 100644 --- a/accessible/html/HTMLSelectAccessible.cpp +++ b/accessible/html/HTMLSelectAccessible.cpp @@ -6,6 +6,7 @@ #include "HTMLSelectAccessible.h" #include "LocalAccessible-inl.h" +#include "DocAccessible-inl.h" #include "nsAccessibilityService.h" #include "nsAccUtils.h" #include "DocAccessible.h" diff --git a/accessible/html/HTMLTableAccessible.cpp b/accessible/html/HTMLTableAccessible.cpp index 2c3dc6b82d..f4b8f9964a 100644 --- a/accessible/html/HTMLTableAccessible.cpp +++ b/accessible/html/HTMLTableAccessible.cpp @@ -12,6 +12,7 @@ #include "ARIAMap.h" #include "CacheConstants.h" #include "LocalAccessible-inl.h" +#include "DocAccessible-inl.h" #include "nsTextEquivUtils.h" #include "Relation.h" #include "mozilla/a11y/Role.h" diff --git a/accessible/interfaces/nsIAccessibleCaretMoveEvent.idl b/accessible/interfaces/nsIAccessibleCaretMoveEvent.idl index 4cdece16cc..2f750002a2 100644 --- a/accessible/interfaces/nsIAccessibleCaretMoveEvent.idl +++ b/accessible/interfaces/nsIAccessibleCaretMoveEvent.idl @@ -19,12 +19,12 @@ interface nsIAccessibleCaretMoveEvent: nsIAccessibleEvent /** * Return true if there is no selection. */ - readonly attribute bool isSelectionCollapsed; + readonly attribute boolean isSelectionCollapsed; /** * Return true if the caret is at the end of a line. */ - readonly attribute bool isAtEndOfLine; + readonly attribute boolean isAtEndOfLine; /** * Return caret move granularity. diff --git a/accessible/interfaces/nsIAccessibleMacInterface.idl b/accessible/interfaces/nsIAccessibleMacInterface.idl index 384d09d5f0..952ac2a9fe 100644 --- a/accessible/interfaces/nsIAccessibleMacInterface.idl +++ b/accessible/interfaces/nsIAccessibleMacInterface.idl @@ -67,7 +67,7 @@ interface nsIAccessibleMacInterface : nsISupports * Returns true if the given attribute is settable on the object. * Emulates `AXUIElementIsAttributeSettable`. **/ - bool isAttributeSettable(in AString attributeName); + boolean isAttributeSettable(in AString attributeName); /** * Sets the given attribute with the given value on the object. diff --git a/accessible/interfaces/nsIAccessibleRole.idl b/accessible/interfaces/nsIAccessibleRole.idl index 9c3376ab48..192f93f01d 100644 --- a/accessible/interfaces/nsIAccessibleRole.idl +++ b/accessible/interfaces/nsIAccessibleRole.idl @@ -806,4 +806,11 @@ interface nsIAccessibleRole : nsISupports * by using methods of two-dimensional navigation. */ const unsigned long ROLE_GRID = 138; + + /** + * Represents a structure containing one or more row elements in a tabular + * container. It is the structural equivalent to the thead, tfoot, and tbody + * elements in an HTML table element. + */ + const unsigned long ROLE_ROWGROUP = 139; }; diff --git a/accessible/ios/MUIAccessible.mm b/accessible/ios/MUIAccessible.mm index 46c4712e2e..d72ed77172 100644 --- a/accessible/ios/MUIAccessible.mm +++ b/accessible/ios/MUIAccessible.mm @@ -159,7 +159,8 @@ static bool isAccessibilityElementInternal(Accessible* aAccessible) { IsAccessibilityElementRule rule = IsAccessibilityElementRule::No; #define ROLE(_geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::_geckoRole: \ rule = iosIsElement; \ break; diff --git a/accessible/mac/MOXAccessibleProtocol.h b/accessible/mac/MOXAccessibleProtocol.h index e3535c1628..51ad3ccef4 100644 --- a/accessible/mac/MOXAccessibleProtocol.h +++ b/accessible/mac/MOXAccessibleProtocol.h @@ -335,6 +335,9 @@ // AXARIARelevant - (NSString* _Nullable)moxARIARelevant; +// AXPlaceholderValue +- (NSString* _Nullable)moxPlaceholderValue; + // AXMozDebugDescription - (NSString* _Nullable)moxMozDebugDescription; diff --git a/accessible/mac/mozAccessible.h b/accessible/mac/mozAccessible.h index 175e25d508..c5e3b7d4f8 100644 --- a/accessible/mac/mozAccessible.h +++ b/accessible/mac/mozAccessible.h @@ -216,6 +216,9 @@ enum CheckedState { - (NSString*)moxARIARelevant; // override +- (NSString*)moxPlaceholderValue; + +// override - (id)moxTitleUIElement; // override diff --git a/accessible/mac/mozAccessible.mm b/accessible/mac/mozAccessible.mm index aa98496511..0609cc7afc 100644 --- a/accessible/mac/mozAccessible.mm +++ b/accessible/mac/mozAccessible.mm @@ -295,7 +295,8 @@ using namespace mozilla::a11y; - (NSString*)moxRole { #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::geckoRole: \ return macRole; @@ -366,7 +367,8 @@ using namespace mozilla::a11y; } #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::geckoRole: \ if (![macSubrole isEqualToString:NSAccessibilityUnknownSubrole]) { \ return macSubrole; \ @@ -646,6 +648,16 @@ struct RoleDescrComparator { return @"additions text"; } +- (NSString*)moxPlaceholderValue { + // First, check for plaecholder HTML attribute + if (NSString* placeholder = utils::GetAccAttr(self, nsGkAtoms::placeholder)) { + return placeholder; + } + + // If no placeholder HTML attribute, check for the aria version. + return utils::GetAccAttr(self, nsGkAtoms::aria_placeholder); +} + - (id)moxTitleUIElement { MOZ_ASSERT(mGeckoAccessible); diff --git a/accessible/tests/browser/.eslintrc.js b/accessible/tests/browser/.eslintrc.js index 528797cb91..96c0859468 100644 --- a/accessible/tests/browser/.eslintrc.js +++ b/accessible/tests/browser/.eslintrc.js @@ -18,7 +18,7 @@ module.exports = { "no-proto": "error", "no-return-assign": "error", "no-shadow": "error", - "no-unused-vars": ["error", { vars: "all", args: "none" }], + "no-unused-vars": ["error", { vars: "all", argsIgnorePattern: "^_" }], "one-var": ["error", "never"], radix: "error", strict: ["error", "global"], diff --git a/accessible/tests/browser/bounds/browser.toml b/accessible/tests/browser/bounds/browser.toml index da1fe37a1e..c4c421cdeb 100644 --- a/accessible/tests/browser/bounds/browser.toml +++ b/accessible/tests/browser/bounds/browser.toml @@ -3,7 +3,6 @@ subsuite = "a11y" support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", - "!/accessible/tests/browser/*.jsm", "!/accessible/tests/mochitest/*.js", "!/accessible/tests/mochitest/letters.gif", ] diff --git a/accessible/tests/browser/e10s/browser.toml b/accessible/tests/browser/e10s/browser.toml index 914f839993..dff9b1c712 100644 --- a/accessible/tests/browser/e10s/browser.toml +++ b/accessible/tests/browser/e10s/browser.toml @@ -10,7 +10,6 @@ support-files = [ "doc_treeupdate_whitespace.html", "fonts/Ahem.sjs", "!/accessible/tests/browser/shared-head.js", - "!/accessible/tests/browser/*.jsm", "!/accessible/tests/mochitest/*.js", "!/accessible/tests/mochitest/events/slow_image.sjs", "!/accessible/tests/mochitest/letters.gif", diff --git a/accessible/tests/browser/e10s/browser_aria_activedescendant.js b/accessible/tests/browser/e10s/browser_aria_activedescendant.js index f58c5aab39..fadda0f964 100644 --- a/accessible/tests/browser/e10s/browser_aria_activedescendant.js +++ b/accessible/tests/browser/e10s/browser_aria_activedescendant.js @@ -277,7 +277,7 @@ async function basicListboxTest(browser, elementReflection) { addAccessibleTask( LISTBOX_MARKUP, - async function (browser, docAcc) { + async function (browser) { info("Test aria-activedescendant content attribute"); await basicListboxTest(browser, false); @@ -303,7 +303,7 @@ addAccessibleTask( addAccessibleTask( LISTBOX_MARKUP, - async function (browser, docAcc) { + async function (browser) { info("Test ariaActiveDescendantElement element reflection"); await basicListboxTest(browser, true); }, @@ -316,7 +316,7 @@ addAccessibleTask( <div role="listbox"> <div role="option" id="activedesc_nondesc_option">option</div> </div>`, - async function (browser, docAcc) { + async function (browser) { info("Test aria-activedescendant non-descendant"); await synthFocus( browser, @@ -348,7 +348,7 @@ addAccessibleTask( item.setAttribute("role", "option"); listbox.appendChild(item); </script>`, - async function (browser, docAcc) { + async function (browser) { info("Test aria-activedescendant in shadow root"); // We want to retrieve elements using their IDs inside the shadow root, so // we define a custom get element by ID method that our utility functions @@ -448,7 +448,7 @@ customElements.define("custom-listbox", } ); </script>`, - async function (browser, docAcc) { + async function (browser) { await synthFocus(browser, "custom-listbox1", "l1_3"); let evtProm = Promise.all([ diff --git a/accessible/tests/browser/e10s/browser_caching_value.js b/accessible/tests/browser/e10s/browser_caching_value.js index 2b968b5948..926110199e 100644 --- a/accessible/tests/browser/e10s/browser_caching_value.js +++ b/accessible/tests/browser/e10s/browser_caching_value.js @@ -46,16 +46,6 @@ const valueTests = [ expected: ["5", 5, 0, 7, 0], }, { - desc: "Value should change when currentValue is called", - id: "slider", - async action(browser, acc) { - acc.QueryInterface(nsIAccessibleValue); - acc.currentValue = 4; - }, - waitFor: EVENT_VALUE_CHANGE, - expected: ["4", 4, 0, 7, 0], - }, - { desc: "Value should change when @aria-valuenow is updated", id: "slider", attrs: [ diff --git a/accessible/tests/browser/events/browser.toml b/accessible/tests/browser/events/browser.toml index 7ec3c3621a..ffbb96cfbb 100644 --- a/accessible/tests/browser/events/browser.toml +++ b/accessible/tests/browser/events/browser.toml @@ -4,12 +4,13 @@ support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", "!/accessible/tests/mochitest/*.js", - "!/accessible/tests/browser/*.jsm", ] prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"] ["browser_alert.js"] +["browser_content_prompt.js"] + ["browser_test_A11yUtils_announce.js"] ["browser_test_caret_move_granularity.js"] diff --git a/accessible/tests/browser/events/browser_content_prompt.js b/accessible/tests/browser/events/browser_content_prompt.js new file mode 100644 index 0000000000..7677c0258a --- /dev/null +++ b/accessible/tests/browser/events/browser_content_prompt.js @@ -0,0 +1,48 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Want to test relations. +/* import-globals-from ../../mochitest/name.js */ +/* import-globals-from ../../mochitest/relations.js */ +/* import-globals-from ../../mochitest/role.js */ +loadScripts( + { name: "relations.js", dir: MOCHITESTS_DIR }, + { name: "name.js", dir: MOCHITESTS_DIR }, + { name: "role.js", dir: MOCHITESTS_DIR } +); + +addAccessibleTask(``, async function (browser) { + info("Showing alert"); + let shown = waitForEvent( + EVENT_SHOW, + evt => evt.accessible.role == ROLE_INTERNAL_FRAME + ); + // Let's make sure the dialog content gets focus. + // On macOS, we unfortunately focus the label. We focus the OK button on + // all other platforms. See https://phabricator.services.mozilla.com/D204908 + // for more discussion. + let expectedRole = + AppConstants.platform == "macosx" ? ROLE_LABEL : ROLE_PUSHBUTTON; + let focused = waitForEvent(EVENT_FOCUS, evt => { + return evt.accessible.role == expectedRole; + }); + await invokeContentTask(browser, [], () => { + // Use setTimeout to avoid blocking the return of the content task + // on the alert, which is otherwise synchronous. + content.setTimeout(() => content.alert("test"), 0); + }); + const frame = (await shown).accessible; + const focusedEl = (await focused).accessible; + ok(true, "Dialog shown and something got focused"); + let dialog = getAccessible(focusedEl.DOMNode.ownerDocument); + testRole(dialog, ROLE_DIALOG); + let infoBody = focusedEl.DOMNode.ownerDocument.getElementById("infoBody"); + testRelation(dialog, RELATION_DESCRIBED_BY, infoBody); + testDescr(dialog, "test "); + info("Dismissing alert"); + let hidden = waitForEvent(EVENT_HIDE, frame); + EventUtils.synthesizeKey("KEY_Escape", {}, frame.DOMNode.contentWindow); + await hidden; +}); diff --git a/accessible/tests/browser/fission/browser.toml b/accessible/tests/browser/fission/browser.toml index 0332573db9..f795ec6b3d 100644 --- a/accessible/tests/browser/fission/browser.toml +++ b/accessible/tests/browser/fission/browser.toml @@ -3,7 +3,6 @@ subsuite = "a11y" support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", - "!/accessible/tests/browser/*.jsm", "!/accessible/tests/mochitest/*.js", ] prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"] diff --git a/accessible/tests/browser/hittest/browser.toml b/accessible/tests/browser/hittest/browser.toml index 7f82a6ff49..894bf05c39 100644 --- a/accessible/tests/browser/hittest/browser.toml +++ b/accessible/tests/browser/hittest/browser.toml @@ -3,7 +3,6 @@ subsuite = "a11y" support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", - "!/accessible/tests/browser/*.jsm", "!/accessible/tests/mochitest/*.js", "!/accessible/tests/mochitest/letters.gif", ] diff --git a/accessible/tests/browser/mac/browser.toml b/accessible/tests/browser/mac/browser.toml index b180a42ab7..227575fb4d 100644 --- a/accessible/tests/browser/mac/browser.toml +++ b/accessible/tests/browser/mac/browser.toml @@ -9,7 +9,6 @@ support-files = [ "doc_menulist.xhtml", "doc_tree.xhtml", "!/accessible/tests/browser/shared-head.js", - "!/accessible/tests/browser/*.jsm", "!/accessible/tests/mochitest/*.js", "!/accessible/tests/mochitest/letters.gif", "!/accessible/tests/mochitest/moz.png", @@ -29,6 +28,8 @@ https_first_disabled = true ["browser_aria_haspopup.js"] +["browser_aria_placeholder.js"] + ["browser_aria_setsize.js"] ["browser_attributed_text.js"] diff --git a/accessible/tests/browser/mac/browser_aria_placeholder.js b/accessible/tests/browser/mac/browser_aria_placeholder.js new file mode 100644 index 0000000000..e5102fd093 --- /dev/null +++ b/accessible/tests/browser/mac/browser_aria_placeholder.js @@ -0,0 +1,70 @@ +/* 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/. */ + +"use strict"; + +/** + * Test that inputs with placeholder text expose it via AXPlaceholderValue and + * correctly _avoid_ exposing it via AXTItle, AXValue, or AXDescription. Verify AXPlaceholderValue is not exposed when the placeholder is used as the name. + */ +addAccessibleTask( + ` + <input id="input" placeholder="Name"><br> + <label for="input2">Enter Name</label><input id="input2" placeholder="Name" value="Elmer Fudd"> + `, + (browser, accDoc) => { + let input = getNativeInterface(accDoc, "input"); + let input2 = getNativeInterface(accDoc, "input2"); + + is( + input.getAttributeValue("AXPlaceholderValue"), + null, + "Placeholder is used as name, so no AXPlaceholderValue is stored." + ); + is(input.getAttributeValue("AXDescription"), "Name"); + is(input.getAttributeValue("AXTitle"), "", "Correct title"); + is(input.getAttributeValue("AXValue"), "", "Correct value"); + + is( + input2.getAttributeValue("AXPlaceholderValue"), + "Name", + "Correct placeholder value in presence of value" + ); + is( + input2.getAttributeValue("AXDescription"), + "Enter Name", + "Correct label" + ); + is(input2.getAttributeValue("AXTitle"), "", "Correct title"); + is(input2.getAttributeValue("AXValue"), "Elmer Fudd", "Correct value"); + } +); + +/** + * Test that aria-placeholder gets exposed via AXPlaceholderValue and correctly + * contributes to AXValue (but not title or description). + */ +addAccessibleTask( + ` + <span id="date-of-birth">Birthday</span> + <div + id="bday" + contenteditable + role="textbox" + aria-labelledby="date-of-birth" + aria-placeholder="MM-DD-YYYY">MM-DD-YYYY</div> + `, + (browser, accDoc) => { + let bday = getNativeInterface(accDoc, "bday"); + + is( + bday.getAttributeValue("AXPlaceholderValue"), + "MM-DD-YYYY", + "Correct placeholder value" + ); + is(bday.getAttributeValue("AXDescription"), "Birthday", "Correct label"); + is(bday.getAttributeValue("AXTitle"), "", "Correct title"); + is(bday.getAttributeValue("AXValue"), "MM-DD-YYYY", "Correct value"); + } +); diff --git a/accessible/tests/browser/mac/browser_roles_elements.js b/accessible/tests/browser/mac/browser_roles_elements.js index b6049e7afd..65caf308c4 100644 --- a/accessible/tests/browser/mac/browser_roles_elements.js +++ b/accessible/tests/browser/mac/browser_roles_elements.js @@ -73,8 +73,8 @@ addAccessibleTask( <div id="switch" role="switch"></div> <div id="timer" role="timer"></div> <div id="tooltip" role="tooltip"></div> - <input type="radio" role="menuitemradio" id="menuitemradio"> - <input type="checkbox" role="menuitemcheckbox" id="menuitemcheckbox"> + <div role="menu"><input type="radio" role="menuitemradio" id="menuitemradio"></div> + <div role="menu"><input type="checkbox" role="menuitemcheckbox" id="menuitemcheckbox"></div> <input type="datetime-local" id="datetime"> <!-- text entries --> diff --git a/accessible/tests/browser/mac/browser_selectables.js b/accessible/tests/browser/mac/browser_selectables.js index af88c2e136..cc489e566d 100644 --- a/accessible/tests/browser/mac/browser_selectables.js +++ b/accessible/tests/browser/mac/browser_selectables.js @@ -300,9 +300,6 @@ addAccessibleTask( async (browser, accDoc) => { let select = getNativeInterface(accDoc, "select"); let one = getNativeInterface(accDoc, "one"); - let two = getNativeInterface(accDoc, "two"); - let three = getNativeInterface(accDoc, "three"); - let four = getNativeInterface(accDoc, "four"); is( select.getAttributeValue("AXTitle"), @@ -339,25 +336,5 @@ addAccessibleTask( }); await evt; is(select.getAttributeValue("AXSelectedChildren").length, 0); - evt = waitForMacEvent("AXSelectedChildrenChanged"); - three.setAttributeValue("AXSelected", true); - await evt; - is(select.getAttributeValue("AXSelectedChildren").length, 1); - ok(getSelectedIds(select).includes("three"), "'three' is selected"); - evt = waitForMacEvent("AXSelectedChildrenChanged"); - select.setAttributeValue("AXSelectedChildren", [one, two]); - await evt; - await untilCacheOk(() => { - let ids = getSelectedIds(select); - return ids[0] == "one" && ids[1] == "two"; - }, "Got correct selected children"); - - evt = waitForMacEvent("AXSelectedChildrenChanged"); - select.setAttributeValue("AXSelectedChildren", [three, two, four]); - await evt; - await untilCacheOk(() => { - let ids = getSelectedIds(select); - return ids[0] == "two" && ids[1] == "three"; - }, "Got correct selected children"); } ); diff --git a/accessible/tests/browser/role/browser_computedARIARole.js b/accessible/tests/browser/role/browser_computedARIARole.js index 50cfe43c98..a87d0a0eae 100644 --- a/accessible/tests/browser/role/browser_computedARIARole.js +++ b/accessible/tests/browser/role/browser_computedARIARole.js @@ -14,7 +14,9 @@ addAccessibleTask( <div id="ariaDirectory" role="directory">ARIA directory</div> <div id="ariaAlertdialog" role="alertdialog">ARIA alertdialog</div> <div id="ariaFeed" role="feed">ARIA feed</div> -<div id="ariaRowgroup" role="rowgroup">ARIA rowgroup</div> +<div role="table"> + <div id="ariaRowgroup" role="rowgroup">ARIA rowgroup</div> +</div> <div id="ariaSearchbox" role="searchbox">ARIA searchbox</div> <div id="ariaUnknown" role="unknown">unknown ARIA role</div> <button id="htmlButton">HTML button</button> diff --git a/accessible/tests/browser/scroll/browser.toml b/accessible/tests/browser/scroll/browser.toml index ef637fe9a5..390e07db8e 100644 --- a/accessible/tests/browser/scroll/browser.toml +++ b/accessible/tests/browser/scroll/browser.toml @@ -3,7 +3,6 @@ subsuite = "a11y" support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", - "!/accessible/tests/browser/*.jsm", "!/accessible/tests/mochitest/*.js", ] prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"] diff --git a/accessible/tests/browser/selectable/browser.toml b/accessible/tests/browser/selectable/browser.toml index 38a13c25f4..144555b6a0 100644 --- a/accessible/tests/browser/selectable/browser.toml +++ b/accessible/tests/browser/selectable/browser.toml @@ -3,7 +3,6 @@ subsuite = "a11y" support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", - "!/accessible/tests/browser/*.jsm", "!/accessible/tests/mochitest/*.js", ] prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"] diff --git a/accessible/tests/browser/selectable/browser_test_aria_select.js b/accessible/tests/browser/selectable/browser_test_aria_select.js index f52603d1cb..dbc36956f8 100644 --- a/accessible/tests/browser/selectable/browser_test_aria_select.js +++ b/accessible/tests/browser/selectable/browser_test_aria_select.js @@ -72,16 +72,16 @@ addAccessibleTask( // role="tablist" aria-multiselectable addAccessibleTask( `<div role="tablist" id="tablist" aria-multiselectable="true"> - <div role="tab" id="tab_multi1">tab1</div> + <div role="tab" id="tab_multi1" aria-selected="true">tab1</div> <div role="tab" id="tab_multi2">tab2</div> + <div role="tab" id="tab_multi3" aria-selected="true">tab3</div> </div>`, async function (browser, docAcc) { info('role="tablist" aria-multiselectable'); let tablist = findAccessibleChildByID(docAcc, "tablist", [ nsIAccessibleSelectable, ]); - - await testMultiSelectable(tablist, ["tab_multi1", "tab_multi2"]); + testSelectableSelection(tablist, ["tab_multi1", "tab_multi3"]); }, { chrome: true, @@ -95,16 +95,16 @@ addAccessibleTask( // role="listbox" aria-multiselectable addAccessibleTask( `<div role="listbox" id="listbox" aria-multiselectable="true"> - <div role="option" id="listbox2_item1">item1</div> + <div role="option" id="listbox2_item1" aria-selected="true">item1</div> <div role="option" id="listbox2_item2">item2</div> + <div role="option" id="listbox2_item3" aria-selected="true">item2</div> </div>`, async function (browser, docAcc) { info('role="listbox" aria-multiselectable'); let listbox = findAccessibleChildByID(docAcc, "listbox", [ nsIAccessibleSelectable, ]); - - await testMultiSelectable(listbox, ["listbox2_item1", "listbox2_item2"]); + testSelectableSelection(listbox, ["listbox2_item1", "listbox2_item3"]); }, { chrome: true, @@ -122,7 +122,7 @@ addAccessibleTask( <thead> <tr> <th tabindex="-1" role="columnheader" id="grid_colhead1" - style="width:6em">Entry #</th> + style="width:6em" aria-selected="true">Entry #</th> <th tabindex="-1" role="columnheader" id="grid_colhead2" style="width:10em">Date</th> <th tabindex="-1" role="columnheader" id="grid_colhead3" @@ -134,7 +134,7 @@ addAccessibleTask( <td tabindex="-1" role="rowheader" id="grid_rowhead" aria-readonly="true">1</td> <td tabindex="-1" role="gridcell" id="grid_cell1" - aria-selected="false">03/14/05</td> + aria-selected="true">03/14/05</td> <td tabindex="-1" role="gridcell" id="grid_cell2" aria-selected="false">Conference Fee</td> </tr> @@ -145,15 +145,7 @@ addAccessibleTask( let grid = findAccessibleChildByID(docAcc, "grid", [ nsIAccessibleSelectable, ]); - - await testMultiSelectable(grid, [ - "grid_colhead1", - "grid_colhead2", - "grid_colhead3", - "grid_rowhead", - "grid_cell1", - "grid_cell2", - ]); + testSelectableSelection(grid, ["grid_colhead1", "grid_cell1"]); }, { chrome: true, diff --git a/accessible/tests/browser/states/browser.toml b/accessible/tests/browser/states/browser.toml index fe29597d27..6b2d20ae14 100644 --- a/accessible/tests/browser/states/browser.toml +++ b/accessible/tests/browser/states/browser.toml @@ -4,7 +4,6 @@ support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", "!/accessible/tests/mochitest/*.js", - "!/accessible/tests/browser/*.jsm", ] prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"] diff --git a/accessible/tests/browser/text/browser.toml b/accessible/tests/browser/text/browser.toml index c04531b126..4bc64866fc 100644 --- a/accessible/tests/browser/text/browser.toml +++ b/accessible/tests/browser/text/browser.toml @@ -3,7 +3,6 @@ subsuite = "a11y" support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", - "!/accessible/tests/browser/*.jsm", "!/accessible/tests/mochitest/*.js", ] prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"] diff --git a/accessible/tests/browser/tree/browser.toml b/accessible/tests/browser/tree/browser.toml index 64be6853d1..a7d9894fcb 100644 --- a/accessible/tests/browser/tree/browser.toml +++ b/accessible/tests/browser/tree/browser.toml @@ -4,7 +4,6 @@ support-files = [ "head.js", "!/accessible/tests/browser/shared-head.js", "!/accessible/tests/mochitest/*.js", - "!/accessible/tests/browser/*.jsm", ] prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"] diff --git a/accessible/tests/browser/windows/a11y_setup.py b/accessible/tests/browser/windows/a11y_setup.py index 726eea07a4..860364d99b 100644 --- a/accessible/tests/browser/windows/a11y_setup.py +++ b/accessible/tests/browser/windows/a11y_setup.py @@ -61,9 +61,6 @@ uiaClient = comtypes.CoCreateInstance( interface=uiaMod.IUIAutomation, clsctx=comtypes.CLSCTX_INPROC_SERVER, ) -TreeScope_Descendants = uiaMod.TreeScope_Descendants -UIA_AutomationIdPropertyId = uiaMod.UIA_AutomationIdPropertyId -del uiaMod def AccessibleObjectFromWindow(hwnd, objectID=OBJID_CLIENT): @@ -173,7 +170,7 @@ class WaitForWinEvent: """ def __init__(self, eventId, match): - """event is the event id to wait for. + """eventId is the event id to wait for. match is either None to match any object, an str containing the DOM id of the desired object, or a function taking a WinEvent which should return True if this is the requested event. @@ -235,6 +232,7 @@ class WaitForWinEvent: raise finally: user32.UnhookWinEvent(self._hook) + ctypes.windll.kernel32.CloseHandle(self._signal) self._proc = None if isinstance(self._matched, Exception): raise self._matched from self._matched @@ -243,17 +241,153 @@ class WaitForWinEvent: def getDocUia(): """Get the IUIAutomationElement for the document being tested.""" - # We start with IAccessible2 because there's no efficient way to - # find the document we want with UIA. - ia2 = getDocIa2() - return uiaClient.ElementFromIAccessible(ia2, CHILDID_SELF) + # There's no efficient way to find the document we want with UIA. We can't + # get the IA2 and then get UIA from that because that will always use the + # IA2 -> UIA proxy, but we don't want that if we're trying to test our + # native implementation. For now, we just search the tree. In future, we + # could perhaps implement a custom property. + hwnd = getFirefoxHwnd() + root = uiaClient.ElementFromHandle(hwnd) + doc = findUiaByDomId(root, "body") + if not doc: + # Sometimes, when UIA is disabled, we can't find the document for some + # unknown reason. Since this only happens when UIA is disabled, we want + # the IA2 -> UIA proxy anyway, so we can start with IA2 in this case. + info("getUiaDoc: Falling back to IA2") # noqa: F821 + ia2 = getDocIa2() + return uiaClient.ElementFromIAccessible(ia2, CHILDID_SELF) + child = uiaClient.RawViewWalker.GetFirstChildElement(doc) + if child and child.CurrentAutomationId == "default-iframe-id": + # This is an iframe or remoteIframe test. + doc = uiaClient.RawViewWalker.GetFirstChildElement(child) + return doc def findUiaByDomId(root, id): - cond = uiaClient.CreatePropertyCondition(UIA_AutomationIdPropertyId, id) + cond = uiaClient.CreatePropertyCondition(uiaMod.UIA_AutomationIdPropertyId, id) # FindFirst ignores elements in the raw tree, so we have to use # FindFirstBuildCache to override that, even though we don't want to cache # anything. request = uiaClient.CreateCacheRequest() request.TreeFilter = uiaClient.RawViewCondition - return root.FindFirstBuildCache(TreeScope_Descendants, cond, request) + el = root.FindFirstBuildCache(uiaMod.TreeScope_Descendants, cond, request) + if not el: + return None + # We need to test things that were introduced after UIA was initially + # introduced in Windows 7. + return el.QueryInterface(uiaMod.IUIAutomationElement9) + + +class WaitForUiaEvent(comtypes.COMObject): + """Wait for a UIA event. + This should be used as follows: + 1. Create an instance to wait for the desired event. + 2. Perform the action that should fire the event. + 3. Call wait() on the instance you created in 1) to wait for the event. + """ + + # This tells comtypes which COM interfaces we implement. It will then call + # either `ISomeInterface_SomeMethod` or just `SomeMethod` on this instance + # when that method is called using COM. We use the shorter convention, since + # we don't anticipate method name conflicts with UIA interfaces. + _com_interfaces_ = [ + uiaMod.IUIAutomationFocusChangedEventHandler, + uiaMod.IUIAutomationPropertyChangedEventHandler, + uiaMod.IUIAutomationEventHandler, + ] + + def __init__(self, *, eventId=None, property=None, match=None): + """eventId is the event id to wait for. Alternatively, you can pass + property to wait for a particular property to change. + match is either None to match any object, an str containing the DOM id + of the desired object, or a function taking a IUIAutomationElement which + should return True if this is the requested event. + """ + self._match = match + self._matched = None + # A kernel event used to signal when we get the desired event. + self._signal = ctypes.windll.kernel32.CreateEventW(None, True, False, None) + if eventId == uiaMod.UIA_AutomationFocusChangedEventId: + uiaClient.AddFocusChangedEventHandler(None, self) + elif eventId: + # Generic automation event. + uiaClient.AddAutomationEventHandler( + eventId, + uiaClient.GetRootElement(), + uiaMod.TreeScope_Subtree, + None, + self, + ) + elif property: + uiaClient.AddPropertyChangedEventHandler( + uiaClient.GetRootElement(), + uiaMod.TreeScope_Subtree, + None, + self, + [property], + ) + else: + raise ValueError("No supported event specified") + + def _checkMatch(self, sender): + if isinstance(self._match, str): + try: + if sender.CurrentAutomationId == self._match: + self._matched = sender + except comtypes.COMError: + pass + elif callable(self._match): + try: + if self._match(sender): + self._matched = sender + except Exception as e: + self._matched = e + else: + self._matched = sender + if self._matched: + ctypes.windll.kernel32.SetEvent(self._signal) + + def HandleFocusChangedEvent(self, sender): + self._checkMatch(sender) + + def HandlePropertyChangedEvent(self, sender, propertyId, newValue): + self._checkMatch(sender) + + def HandleAutomationEvent(self, sender, eventId): + self._checkMatch(sender) + + def wait(self): + """Wait for and return the IUIAutomationElement which sent the desired + event.""" + # Pump Windows messages until we get the desired event, which will be + # signalled using a kernel event. + handles = (ctypes.c_void_p * 1)(self._signal) + index = ctypes.wintypes.DWORD() + TIMEOUT = 10000 + try: + ctypes.oledll.ole32.CoWaitForMultipleHandles( + COWAIT_DEFAULT, TIMEOUT, 1, handles, ctypes.byref(index) + ) + except WindowsError as e: + if e.winerror == RPC_S_CALLPENDING: + raise TimeoutError("Timeout before desired event received") + raise + finally: + uiaClient.RemoveAllEventHandlers() + ctypes.windll.kernel32.CloseHandle(self._signal) + if isinstance(self._matched, Exception): + raise self._matched from self._matched + return self._matched + + +def getUiaPattern(element, patternName): + """Get a control pattern interface from an IUIAutomationElement.""" + patternId = getattr(uiaMod, f"UIA_{patternName}PatternId") + unknown = element.GetCurrentPattern(patternId) + if not unknown: + return None + # GetCurrentPattern returns an IUnknown. We have to QI to the real + # interface. + # Get the comtypes interface object. + interface = getattr(uiaMod, f"IUIAutomation{patternName}Pattern") + return unknown.QueryInterface(interface) diff --git a/accessible/tests/browser/windows/uia/browser.toml b/accessible/tests/browser/windows/uia/browser.toml index d1513c1822..75728f56d7 100644 --- a/accessible/tests/browser/windows/uia/browser.toml +++ b/accessible/tests/browser/windows/uia/browser.toml @@ -10,4 +10,9 @@ support-files = ["head.js"] ["browser_elementFromPoint.js"] +["browser_focus.js"] +["browser_generalProps.js"] + +["browser_simplePatterns.js"] + ["browser_tree.js"] diff --git a/accessible/tests/browser/windows/uia/browser_focus.js b/accessible/tests/browser/windows/uia/browser_focus.js new file mode 100644 index 0000000000..f26c9e1d1b --- /dev/null +++ b/accessible/tests/browser/windows/uia/browser_focus.js @@ -0,0 +1,61 @@ +/* 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/. */ + +"use strict"; + +async function testIsFocusable(pyVar, isFocusable) { + const result = await runPython(`${pyVar}.CurrentIsKeyboardFocusable`); + if (isFocusable) { + ok(result, `${pyVar} is focusable`); + } else { + ok(!result, `${pyVar} isn't focusable`); + } +} + +async function testHasFocus(pyVar, hasFocus) { + const result = await runPython(`${pyVar}.CurrentHasKeyboardFocus`); + if (hasFocus) { + ok(result, `${pyVar} has focus`); + } else { + ok(!result, `${pyVar} doesn't have focus`); + } +} + +addUiaTask( + ` +<button id="button1">button1</button> +<p id="p">p</p> +<button id="button2">button2</button> + `, + async function (browser) { + await definePyVar("doc", `getDocUia()`); + await testIsFocusable("doc", true); + await testHasFocus("doc", true); + + await assignPyVarToUiaWithId("button1"); + await testIsFocusable("button1", true); + await testHasFocus("button1", false); + info("Focusing button1"); + await setUpWaitForUiaEvent("AutomationFocusChanged", "button1"); + await invokeFocus(browser, "button1"); + await waitForUiaEvent(); + ok(true, "Got AutomationFocusChanged event on button1"); + await testHasFocus("button1", true); + + await assignPyVarToUiaWithId("p"); + await testIsFocusable("p", false); + await testHasFocus("p", false); + + await assignPyVarToUiaWithId("button2"); + await testIsFocusable("button2", true); + await testHasFocus("button2", false); + info("Focusing button2"); + await setUpWaitForUiaEvent("AutomationFocusChanged", "button2"); + await invokeFocus(browser, "button2"); + await waitForUiaEvent(); + ok(true, "Got AutomationFocusChanged event on button2"); + await testHasFocus("button2", true); + await testHasFocus("button1", false); + } +); diff --git a/accessible/tests/browser/windows/uia/browser_generalProps.js b/accessible/tests/browser/windows/uia/browser_generalProps.js new file mode 100644 index 0000000000..5cfda226d0 --- /dev/null +++ b/accessible/tests/browser/windows/uia/browser_generalProps.js @@ -0,0 +1,105 @@ +/* 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/. */ + +"use strict"; + +/** + * Test the Name property. + */ +addUiaTask( + ` +<button id="button">before</button> +<div id="div">div</div> + `, + async function testName(browser) { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("button"); + is( + await runPython(`button.CurrentName`), + "before", + "button has correct name" + ); + await assignPyVarToUiaWithId("div"); + is(await runPython(`div.CurrentName`), "", "div has no name"); + + info("Setting aria-label on button"); + await setUpWaitForUiaPropEvent("Name", "button"); + await invokeSetAttribute(browser, "button", "aria-label", "after"); + await waitForUiaEvent(); + ok(true, "Got Name prop change event on button"); + is( + await runPython(`button.CurrentName`), + "after", + "button has correct name" + ); + } +); + +/** + * Test the FullDescription property. + */ +addUiaTask( + ` +<button id="button" aria-description="before">button</button> +<div id="div">div</div> + `, + async function testFullDescription(browser) { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("button"); + is( + await runPython(`button.CurrentFullDescription`), + "before", + "button has correct FullDescription" + ); + await assignPyVarToUiaWithId("div"); + is( + await runPython(`div.CurrentFullDescription`), + "", + "div has no FullDescription" + ); + + info("Setting aria-description on button"); + await setUpWaitForUiaPropEvent("FullDescription", "button"); + await invokeSetAttribute(browser, "button", "aria-description", "after"); + await waitForUiaEvent(); + ok(true, "Got FullDescription prop change event on button"); + is( + await runPython(`button.CurrentFullDescription`), + "after", + "button has correct FullDescription" + ); + }, + // The IA2 -> UIA proxy doesn't support FullDescription. + { uiaEnabled: true, uiaDisabled: false } +); + +/** + * Test the IsEnabled property. + */ +addUiaTask( + ` +<button id="button">button</button> +<p id="p">p</p> + `, + async function testIsEnabled(browser) { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("button"); + ok(await runPython(`button.CurrentIsEnabled`), "button has IsEnabled true"); + // The IA2 -> UIA proxy doesn't fire IsEnabled prop change events. + if (gIsUiaEnabled) { + info("Setting disabled on button"); + await setUpWaitForUiaPropEvent("IsEnabled", "button"); + await invokeSetAttribute(browser, "button", "disabled", true); + await waitForUiaEvent(); + ok(true, "Got IsEnabled prop change event on button"); + ok( + !(await runPython(`button.CurrentIsEnabled`)), + "button has IsEnabled false" + ); + } + + await assignPyVarToUiaWithId("p"); + ok(await runPython(`p.CurrentIsEnabled`), "p has IsEnabled true"); + } +); diff --git a/accessible/tests/browser/windows/uia/browser_simplePatterns.js b/accessible/tests/browser/windows/uia/browser_simplePatterns.js new file mode 100644 index 0000000000..f464db0e13 --- /dev/null +++ b/accessible/tests/browser/windows/uia/browser_simplePatterns.js @@ -0,0 +1,445 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../../mochitest/role.js */ +/* import-globals-from ../../../mochitest/states.js */ +loadScripts( + { name: "role.js", dir: MOCHITESTS_DIR }, + { name: "states.js", dir: MOCHITESTS_DIR } +); + +/* eslint-disable camelcase */ +const ExpandCollapseState_Collapsed = 0; +const ExpandCollapseState_Expanded = 1; +const ToggleState_Off = 0; +const ToggleState_On = 1; +const ToggleState_Indeterminate = 2; +/* eslint-enable camelcase */ + +/** + * Test the Invoke pattern. + */ +addUiaTask( + ` +<button id="button">button</button> +<p id="p">p</p> +<input id="checkbox" type="checkbox"> + `, + async function testInvoke() { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("button"); + await definePyVar("pattern", `getUiaPattern(button, "Invoke")`); + ok(await runPython(`bool(pattern)`), "button has Invoke pattern"); + info("Calling Invoke on button"); + // The button will get focus when it is clicked. + let focused = waitForEvent(EVENT_FOCUS, "button"); + // The UIA -> IA2 proxy doesn't fire the Invoked event. + if (gIsUiaEnabled) { + await setUpWaitForUiaEvent("Invoke_Invoked", "button"); + } + await runPython(`pattern.Invoke()`); + await focused; + ok(true, "button got focus"); + if (gIsUiaEnabled) { + await waitForUiaEvent(); + ok(true, "button got Invoked event"); + } + + await testPatternAbsent("p", "Invoke"); + // The Microsoft IA2 -> UIA proxy doesn't follow Microsoft's own rules. + if (gIsUiaEnabled) { + // Check boxes expose the Toggle pattern, so they should not expose the + // Invoke pattern. + await testPatternAbsent("checkbox", "Invoke"); + } + } +); + +/** + * Test the Toggle pattern. + */ +addUiaTask( + ` +<input id="checkbox" type="checkbox" checked> +<button id="toggleButton" aria-pressed="false">toggle</button> +<button id="button">button</button> +<p id="p">p</p> + +<script> + // When checkbox is clicked and it is not checked, make it indeterminate. + document.getElementById("checkbox").addEventListener("click", evt => { + // Within the event listener, .checked is reversed and you can't set + // .indeterminate. Work around this by deferring and handling the changes + // ourselves. + evt.preventDefault(); + const target = evt.target; + setTimeout(() => { + if (target.checked) { + target.checked = false; + } else { + target.indeterminate = true; + } + }, 0); + }); + + // When toggleButton is clicked, set aria-pressed to true. + document.getElementById("toggleButton").addEventListener("click", evt => { + evt.target.ariaPressed = "true"; + }); +</script> + `, + async function testToggle() { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("checkbox"); + await definePyVar("pattern", `getUiaPattern(checkbox, "Toggle")`); + ok(await runPython(`bool(pattern)`), "checkbox has Toggle pattern"); + is( + await runPython(`pattern.CurrentToggleState`), + ToggleState_On, + "checkbox has ToggleState_On" + ); + // The IA2 -> UIA proxy doesn't fire ToggleState prop change events. + if (gIsUiaEnabled) { + info("Calling Toggle on checkbox"); + await setUpWaitForUiaPropEvent("ToggleToggleState", "checkbox"); + await runPython(`pattern.Toggle()`); + await waitForUiaEvent(); + ok(true, "Got ToggleState prop change event on checkbox"); + is( + await runPython(`pattern.CurrentToggleState`), + ToggleState_Off, + "checkbox has ToggleState_Off" + ); + info("Calling Toggle on checkbox"); + await setUpWaitForUiaPropEvent("ToggleToggleState", "checkbox"); + await runPython(`pattern.Toggle()`); + await waitForUiaEvent(); + ok(true, "Got ToggleState prop change event on checkbox"); + is( + await runPython(`pattern.CurrentToggleState`), + ToggleState_Indeterminate, + "checkbox has ToggleState_Indeterminate" + ); + } + + await assignPyVarToUiaWithId("toggleButton"); + await definePyVar("pattern", `getUiaPattern(toggleButton, "Toggle")`); + ok(await runPython(`bool(pattern)`), "toggleButton has Toggle pattern"); + is( + await runPython(`pattern.CurrentToggleState`), + ToggleState_Off, + "toggleButton has ToggleState_Off" + ); + if (gIsUiaEnabled) { + info("Calling Toggle on toggleButton"); + await setUpWaitForUiaPropEvent("ToggleToggleState", "toggleButton"); + await runPython(`pattern.Toggle()`); + await waitForUiaEvent(); + ok(true, "Got ToggleState prop change event on toggleButton"); + is( + await runPython(`pattern.CurrentToggleState`), + ToggleState_On, + "toggleButton has ToggleState_Off" + ); + } + + await testPatternAbsent("button", "Toggle"); + await testPatternAbsent("p", "Toggle"); + } +); + +/** + * Test the ExpandCollapse pattern. + */ +addUiaTask( + ` +<details> + <summary id="summary">summary</summary> + details +</details> +<button id="popup" aria-haspopup="true">popup</button> +<button id="button">button</button> +<script> + // When popup is clicked, set aria-expanded to true. + document.getElementById("popup").addEventListener("click", evt => { + evt.target.ariaExpanded = "true"; + }); +</script> + `, + async function testExpandCollapse() { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("summary"); + await definePyVar("pattern", `getUiaPattern(summary, "ExpandCollapse")`); + ok(await runPython(`bool(pattern)`), "summary has ExpandCollapse pattern"); + is( + await runPython(`pattern.CurrentExpandCollapseState`), + ExpandCollapseState_Collapsed, + "summary has ExpandCollapseState_Collapsed" + ); + // The IA2 -> UIA proxy doesn't fire ToggleState prop change events, nor + // does it fail when Expand/Collapse is called on a control which is + // already in the desired state. + if (gIsUiaEnabled) { + info("Calling Expand on summary"); + await setUpWaitForUiaPropEvent( + "ExpandCollapseExpandCollapseState", + "summary" + ); + await runPython(`pattern.Expand()`); + await waitForUiaEvent(); + ok( + true, + "Got ExpandCollapseExpandCollapseState prop change event on summary" + ); + is( + await runPython(`pattern.CurrentExpandCollapseState`), + ExpandCollapseState_Expanded, + "summary has ExpandCollapseState_Expanded" + ); + info("Calling Expand on summary"); + await testPythonRaises(`pattern.Expand()`, "Expand on summary failed"); + info("Calling Collapse on summary"); + await setUpWaitForUiaPropEvent( + "ExpandCollapseExpandCollapseState", + "summary" + ); + await runPython(`pattern.Collapse()`); + await waitForUiaEvent(); + ok( + true, + "Got ExpandCollapseExpandCollapseState prop change event on summary" + ); + is( + await runPython(`pattern.CurrentExpandCollapseState`), + ExpandCollapseState_Collapsed, + "summary has ExpandCollapseState_Collapsed" + ); + info("Calling Collapse on summary"); + await testPythonRaises( + `pattern.Collapse()`, + "Collapse on summary failed" + ); + } + + await assignPyVarToUiaWithId("popup"); + // Initially, popup has aria-haspopup but not aria-expanded. That should + // be exposed as collapsed. + await definePyVar("pattern", `getUiaPattern(popup, "ExpandCollapse")`); + ok(await runPython(`bool(pattern)`), "popup has ExpandCollapse pattern"); + // The IA2 -> UIA proxy doesn't expose ExpandCollapseState_Collapsed for + // aria-haspopup without aria-expanded. + if (gIsUiaEnabled) { + is( + await runPython(`pattern.CurrentExpandCollapseState`), + ExpandCollapseState_Collapsed, + "popup has ExpandCollapseState_Collapsed" + ); + info("Calling Expand on popup"); + await setUpWaitForUiaPropEvent( + "ExpandCollapseExpandCollapseState", + "popup" + ); + await runPython(`pattern.Expand()`); + await waitForUiaEvent(); + ok( + true, + "Got ExpandCollapseExpandCollapseState prop change event on popup" + ); + is( + await runPython(`pattern.CurrentExpandCollapseState`), + ExpandCollapseState_Expanded, + "popup has ExpandCollapseState_Expanded" + ); + } + + await testPatternAbsent("button", "ExpandCollapse"); + } +); + +/** + * Test the ScrollItem pattern. + */ +addUiaTask( + ` +<hr style="height: 100vh;"> +<button id="button">button</button> + `, + async function testScrollItem(browser, docAcc) { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("button"); + await definePyVar("pattern", `getUiaPattern(button, "ScrollItem")`); + ok(await runPython(`bool(pattern)`), "button has ScrollItem pattern"); + const button = findAccessibleChildByID(docAcc, "button"); + testStates(button, STATE_OFFSCREEN); + info("Calling ScrollIntoView on button"); + // UIA doesn't have an event for this. + let scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc); + await runPython(`pattern.ScrollIntoView()`); + await scrolled; + ok(true, "Document scrolled"); + testStates(button, 0, 0, STATE_OFFSCREEN); + } +); + +/** + * Test the Value pattern. + */ +addUiaTask( + ` +<input id="text" value="before"> +<input id="textRo" readonly value="textRo"> +<input id="textDis" disabled value="textDis"> +<select id="select"><option selected>a</option><option>b</option></select> +<progress id="progress" value="0.5"></progress> +<input id="range" type="range" aria-valuetext="02:00:00"> +<a id="link" href="https://example.com/">Link</a> +<div id="ariaTextbox" contenteditable role="textbox">before</div> +<button id="button">button</button> + `, + async function testValue() { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("text"); + await definePyVar("pattern", `getUiaPattern(text, "Value")`); + ok(await runPython(`bool(pattern)`), "text has Value pattern"); + ok( + !(await runPython(`pattern.CurrentIsReadOnly`)), + "text has IsReadOnly false" + ); + is( + await runPython(`pattern.CurrentValue`), + "before", + "text has correct Value" + ); + info("SetValue on text"); + await setUpWaitForUiaPropEvent("ValueValue", "text"); + await runPython(`pattern.SetValue("after")`); + await waitForUiaEvent(); + is( + await runPython(`pattern.CurrentValue`), + "after", + "text has correct Value" + ); + + await assignPyVarToUiaWithId("textRo"); + await definePyVar("pattern", `getUiaPattern(textRo, "Value")`); + ok(await runPython(`bool(pattern)`), "textRo has Value pattern"); + ok( + await runPython(`pattern.CurrentIsReadOnly`), + "textRo has IsReadOnly true" + ); + is( + await runPython(`pattern.CurrentValue`), + "textRo", + "textRo has correct Value" + ); + info("SetValue on textRo"); + await testPythonRaises( + `pattern.SetValue("after")`, + "SetValue on textRo failed" + ); + + await assignPyVarToUiaWithId("textDis"); + await definePyVar("pattern", `getUiaPattern(textDis, "Value")`); + ok(await runPython(`bool(pattern)`), "textDis has Value pattern"); + ok( + !(await runPython(`pattern.CurrentIsReadOnly`)), + "textDis has IsReadOnly false" + ); + is( + await runPython(`pattern.CurrentValue`), + "textDis", + "textDis has correct Value" + ); + // The IA2 -> UIA proxy doesn't fail SetValue for a disabled element. + if (gIsUiaEnabled) { + info("SetValue on textDis"); + await testPythonRaises( + `pattern.SetValue("after")`, + "SetValue on textDis failed" + ); + } + + await assignPyVarToUiaWithId("select"); + await definePyVar("pattern", `getUiaPattern(select, "Value")`); + ok(await runPython(`bool(pattern)`), "select has Value pattern"); + ok( + !(await runPython(`pattern.CurrentIsReadOnly`)), + "select has IsReadOnly false" + ); + is( + await runPython(`pattern.CurrentValue`), + "a", + "select has correct Value" + ); + info("SetValue on select"); + await testPythonRaises( + `pattern.SetValue("b")`, + "SetValue on select failed" + ); + + await assignPyVarToUiaWithId("progress"); + await definePyVar("pattern", `getUiaPattern(progress, "Value")`); + ok(await runPython(`bool(pattern)`), "progress has Value pattern"); + // Gecko a11y doesn't treat progress bars as read only, but it probably + // should. + todo( + await runPython(`pattern.CurrentIsReadOnly`), + "progress has IsReadOnly true" + ); + is( + await runPython(`pattern.CurrentValue`), + "50%", + "progress has correct Value" + ); + info("SetValue on progress"); + await testPythonRaises( + `pattern.SetValue("60%")`, + "SetValue on progress failed" + ); + + await assignPyVarToUiaWithId("range"); + await definePyVar("pattern", `getUiaPattern(range, "Value")`); + ok(await runPython(`bool(pattern)`), "range has Value pattern"); + is( + await runPython(`pattern.CurrentValue`), + "02:00:00", + "range has correct Value" + ); + + await assignPyVarToUiaWithId("link"); + await definePyVar("pattern", `getUiaPattern(link, "Value")`); + ok(await runPython(`bool(pattern)`), "link has Value pattern"); + is( + await runPython(`pattern.CurrentValue`), + "https://example.com/", + "link has correct Value" + ); + + await assignPyVarToUiaWithId("ariaTextbox"); + await definePyVar("pattern", `getUiaPattern(ariaTextbox, "Value")`); + ok(await runPython(`bool(pattern)`), "ariaTextbox has Value pattern"); + ok( + !(await runPython(`pattern.CurrentIsReadOnly`)), + "ariaTextbox has IsReadOnly false" + ); + is( + await runPython(`pattern.CurrentValue`), + "before", + "ariaTextbox has correct Value" + ); + info("SetValue on ariaTextbox"); + await setUpWaitForUiaPropEvent("ValueValue", "ariaTextbox"); + await runPython(`pattern.SetValue("after")`); + await waitForUiaEvent(); + is( + await runPython(`pattern.CurrentValue`), + "after", + "ariaTextbox has correct Value" + ); + + await testPatternAbsent("button", "Value"); + } +); diff --git a/accessible/tests/browser/windows/uia/browser_tree.js b/accessible/tests/browser/windows/uia/browser_tree.js index 778609bedb..c63045b6d1 100644 --- a/accessible/tests/browser/windows/uia/browser_tree.js +++ b/accessible/tests/browser/windows/uia/browser_tree.js @@ -13,24 +13,6 @@ async function testIsControl(pyVar, isControl) { } } -/** - * Define a global Python variable and assign it to a given Python expression. - */ -function definePyVar(varName, expression) { - return runPython(` - global ${varName} - ${varName} = ${expression} - `); -} - -/** - * Get the UIA element with the given id and assign it to a global Python - * variable using the id as the variable name. - */ -function assignPyVarToUiaWithId(id) { - return definePyVar(id, `findUiaByDomId(doc, "${id}")`); -} - addUiaTask( ` <p id="p">paragraph</p> @@ -46,7 +28,7 @@ addUiaTask( <div id="editable" contenteditable>editable</div> <table id="table"><tr><th>th</th></tr></table> `, - async function (browser, docAcc) { + async function () { await definePyVar("doc", `getDocUia()`); await assignPyVarToUiaWithId("p"); await testIsControl("p", false); diff --git a/accessible/tests/browser/windows/uia/head.js b/accessible/tests/browser/windows/uia/head.js index e659354c7c..5b453ce6fe 100644 --- a/accessible/tests/browser/windows/uia/head.js +++ b/accessible/tests/browser/windows/uia/head.js @@ -4,7 +4,7 @@ "use strict"; -/* exported gIsUiaEnabled, addUiaTask */ +/* exported gIsUiaEnabled, addUiaTask, definePyVar, assignPyVarToUiaWithId, setUpWaitForUiaEvent, setUpWaitForUiaPropEvent, waitForUiaEvent, testPatternAbsent, testPythonRaises */ // Load the shared-head file first. Services.scriptloader.loadSubScript( @@ -53,3 +53,76 @@ function addUiaTask(doc, task, options = {}) { addTask(false); } } + +/** + * Define a global Python variable and assign it to a given Python expression. + */ +function definePyVar(varName, expression) { + return runPython(` + global ${varName} + ${varName} = ${expression} + `); +} + +/** + * Get the UIA element with the given id and assign it to a global Python + * variable using the id as the variable name. + */ +function assignPyVarToUiaWithId(id) { + return definePyVar(id, `findUiaByDomId(doc, "${id}")`); +} + +/** + * Set up to wait for a UIA event. You must await this before performing the + * action which fires the event. + */ +function setUpWaitForUiaEvent(eventName, id) { + return definePyVar( + "onEvent", + `WaitForUiaEvent(eventId=UIA_${eventName}EventId, match="${id}")` + ); +} + +/** + * Set up to wait for a UIA property change event. You must await this before + * performing the action which fires the event. + */ +function setUpWaitForUiaPropEvent(propName, id) { + return definePyVar( + "onEvent", + `WaitForUiaEvent(property=UIA_${propName}PropertyId, match="${id}")` + ); +} + +/** + * Wait for the event requested in setUpWaitForUia*Event. + */ +function waitForUiaEvent() { + return runPython(` + onEvent.wait() + `); +} + +/** + * Verify that a UIA element does *not* support the given control pattern. + */ +async function testPatternAbsent(id, patternName) { + const hasPattern = await runPython(` + el = findUiaByDomId(doc, "${id}") + return bool(getUiaPattern(el, "${patternName}")) + `); + ok(!hasPattern, `${id} doesn't have ${patternName} pattern`); +} + +/** + * Verify that a Python expression raises an exception. + */ +async function testPythonRaises(expression, message) { + let failed = false; + try { + await runPython(expression); + } catch { + failed = true; + } + ok(failed, message); +} diff --git a/accessible/tests/mochitest/attributes/test_obj_group.xhtml b/accessible/tests/mochitest/attributes/test_obj_group.xhtml index 0eda4b6f2d..ba0c8bd357 100644 --- a/accessible/tests/mochitest/attributes/test_obj_group.xhtml +++ b/accessible/tests/mochitest/attributes/test_obj_group.xhtml @@ -196,17 +196,19 @@ </menulist> <vbox> - <description role="menuitem" id="aria-menuitem" - value="conventional menuitem"/> - <description role="menuitemcheckbox" id="aria-menuitemcheckbox" - value="conventional checkbox menuitem"/> - <description role="menuitem" hidden="true"/> - <description role="menuitemradio" id="aria-menuitemradio" - value="conventional radio menuitem"/> - <description role="separator" - value="conventional separator"/> - <description role="menuitem" id="aria-menuitem2" - value="conventional menuitem"/> + <div role="menu"> + <description role="menuitem" id="aria-menuitem" + value="conventional menuitem"/> + <description role="menuitemcheckbox" id="aria-menuitemcheckbox" + value="conventional checkbox menuitem"/> + <description role="menuitem" hidden="true"/> + <description role="menuitemradio" id="aria-menuitemradio" + value="conventional radio menuitem"/> + <description role="separator" + value="conventional separator"/> + <description role="menuitem" id="aria-menuitem2" + value="conventional menuitem"/> + </div> </vbox> </vbox> diff --git a/accessible/tests/mochitest/focus/test_takeFocus.html b/accessible/tests/mochitest/focus/test_takeFocus.html index 752ed66b36..6384fbfcb8 100644 --- a/accessible/tests/mochitest/focus/test_takeFocus.html +++ b/accessible/tests/mochitest/focus/test_takeFocus.html @@ -48,7 +48,6 @@ gQueue.push(new takeFocusInvoker("aria-link")); gQueue.push(new takeFocusInvoker("aria-link2")); gQueue.push(new takeFocusInvoker("link")); - gQueue.push(new takeFocusInvoker("item2")); gQueue.push(new takeFocusInvoker(document)); gQueue.push(new takeFocusInvoker("lb_item2")); gQueue.push(new takeFocusInvoker(document)); diff --git a/accessible/tests/mochitest/name/test_general.html b/accessible/tests/mochitest/name/test_general.html index 09723e6222..47104ced3e 100644 --- a/accessible/tests/mochitest/name/test_general.html +++ b/accessible/tests/mochitest/name/test_general.html @@ -105,7 +105,7 @@ // The label element contains the button. The name is calculated from // this button. // Note: the name contains the content of the button. - testName("btn_label_inside", "text10text"); + testName("btn_label_inside", "text 10 text"); // The label element and the button are placed in the same form. Gets // the name from the label subtree. @@ -713,17 +713,21 @@ <div id="grouping" role="group">label</div> <button id="requested_name_from_grouping"aria-labelledby="grouping"></button> <!-- Name from sub tree of tbody marked as display:block;, which is also a grouping --> - <div id="listitem_containing_block_tbody" role="listitem"> - <table> - <tbody style="display: block;"> - <tr><td>label</td></tr> - </tbody> - </table> + <div role="list"> + <div id="listitem_containing_block_tbody" role="listitem"> + <table> + <tbody style="display: block;"> + <tr><td>label</td></tr> + </tbody> + </table> + </div> </div> <!-- Name from subtree of treeitem containing grouping --> - <div id="treeitem_containing_grouping" role="treeitem" aria-level="1" aria-expanded="true">root - <div role="group"> - <div role="treeitem" aria-level="2">sub</div> + <div role="tree"> + <div id="treeitem_containing_grouping" role="treeitem" aria-level="1" aria-expanded="true">root + <div role="group"> + <div role="treeitem" aria-level="2">sub</div> + </div> </div> </div> @@ -735,11 +739,13 @@ </div> <!-- Text nodes and inline elements. --> - <div id="container_text_inline" role="option">a<strong>b</strong>c</div> - <!-- Text nodes and block elements. --> - <div id="container_text_block" role="option">a<p>b</p>c</div> - <!-- Text nodes and empty block elements. --> - <div id="container_text_emptyblock" role="option">a<p></p><p></p>b</div> + <div role="listbox"> + <div id="container_text_inline" role="option">a<strong>b</strong>c</div> + <!-- Text nodes and block elements. --> + <div id="container_text_block" role="option">a<p>b</p>c</div> + <!-- Text nodes and empty block elements. --> + <div id="container_text_emptyblock" role="option">a<p></p><p></p>b</div> + </div> <!-- aria-labelledby referring to a slot --> <div id="shadowHost"> diff --git a/accessible/tests/mochitest/relations/a11y.toml b/accessible/tests/mochitest/relations/a11y.toml index ae41bf91a5..77021c28cf 100644 --- a/accessible/tests/mochitest/relations/a11y.toml +++ b/accessible/tests/mochitest/relations/a11y.toml @@ -18,6 +18,4 @@ skip-if = [ ["test_tree.xhtml"] -["test_ui_modalprompt.html"] - ["test_update.html"] diff --git a/accessible/tests/mochitest/relations/test_ui_modalprompt.html b/accessible/tests/mochitest/relations/test_ui_modalprompt.html deleted file mode 100644 index a05b273d86..0000000000 --- a/accessible/tests/mochitest/relations/test_ui_modalprompt.html +++ /dev/null @@ -1,111 +0,0 @@ -<html> - -<head> - <title>Modal prompts</title> - <link rel="stylesheet" type="text/css" - href="chrome://mochikit/content/tests/SimpleTest/test.css" /> - - <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> - <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> - - <script type="application/javascript" - src="../common.js"></script> - <script type="application/javascript" - src="../relations.js"></script> - <script type="application/javascript" - src="../role.js"></script> - <script type="application/javascript" - src="../events.js"></script> - <script type="application/javascript" - src="../browser.js"></script> - - <script type="application/javascript"> - SpecialPowers.pushPrefEnv({ - set: [["prompts.contentPromptSubDialog", false]], - }); - function showAlert() { - this.eventSeq = [ - { - type: EVENT_SHOW, - match(aEvent) { - return aEvent.accessible.role == ROLE_DIALOG; - }, - }, - ]; - - this.invoke = function showAlert_invoke() { - window.setTimeout( - function() { - currentTabDocument().defaultView.alert("hello"); - }, 0); - }; - - this.check = function showAlert_finalCheck(aEvent) { - if(aEvent.type === EVENT_HIDE) { - return; - } - var dialog = aEvent.accessible.DOMNode; - var info = dialog.querySelector(".tabmodalprompt-infoBody"); - testRelation(info, RELATION_DESCRIPTION_FOR, dialog); - testRelation(dialog, RELATION_DESCRIBED_BY, info); - }; - - this.getID = function showAlert_getID() { - return "show alert"; - }; - } - - function closeAlert() { - this.eventSeq = [ - { - type: EVENT_HIDE, - match(aEvent) { - return aEvent.accessible.role == ROLE_DIALOG; - }, - }, - ]; - - this.invoke = function showAlert_invoke() { - synthesizeKey("VK_RETURN", {}, browserWindow()); - }; - - this.getID = function showAlert_getID() { - return "cleanup alert"; - }; - } - - - // gA11yEventDumpToConsole = true; // debug - - var gQueue = null; - function doTests() { - gQueue = new eventQueue(); - gQueue.push(new showAlert()); - gQueue.push(new closeAlert()); - gQueue.onFinish = function() { - closeBrowserWindow(); - }; - gQueue.invoke(); // will call SimpleTest.finish() - } - - SimpleTest.waitForExplicitFinish(); - openBrowserWindow(doTests); - </script> - -</head> - -<body id="body"> - - <a target="_blank" - href="https://bugzilla.mozilla.org/show_bug.cgi?id=661293" - title="The tabmodalprompt dialog's prompt label doesn't get the text properly associated for accessibility"> - Mozilla Bug 661293 - </a> - <br> - <p id="display"></p> - <div id="content" style="display: none"></div> - <pre id="test"> - </pre> - -</body> -</html> diff --git a/accessible/tests/mochitest/role/test_aria.html b/accessible/tests/mochitest/role/test_aria.html index 0e7873c485..dcd2c7839e 100644 --- a/accessible/tests/mochitest/role/test_aria.html +++ b/accessible/tests/mochitest/role/test_aria.html @@ -42,8 +42,6 @@ testRole("aria_checkbox_mixed", ROLE_CHECKBUTTON); testRole("aria_code", ROLE_CODE); testRole("aria_code_mixed", ROLE_CODE); - testRole("aria_columnheader", ROLE_COLUMNHEADER); - testRole("aria_columnheader_mixed", ROLE_COLUMNHEADER); testRole("aria_combobox", ROLE_EDITCOMBOBOX); testRole("aria_combobox_mixed", ROLE_EDITCOMBOBOX); testRole("aria_comment", ROLE_COMMENT); @@ -66,8 +64,6 @@ testRole("aria_figure_mixed", ROLE_FIGURE); testRole("aria_grid", ROLE_GRID); testRole("aria_grid_mixed", ROLE_GRID); - testRole("aria_gridcell", ROLE_GRID_CELL); - testRole("aria_gridcell_mixed", ROLE_GRID_CELL); testRole("aria_group", ROLE_GROUPING); testRole("aria_group_mixed", ROLE_GROUPING); testRole("aria_heading", ROLE_HEADING); @@ -82,8 +78,6 @@ testRole("aria_list_mixed", ROLE_LIST); testRole("aria_listbox", ROLE_LISTBOX); testRole("aria_listbox_mixed", ROLE_LISTBOX); - testRole("aria_listitem", ROLE_LISTITEM); - testRole("aria_listitem_mixed", ROLE_LISTITEM); testRole("aria_log", ROLE_TEXT); // weak role testRole("aria_log_mixed", ROLE_TEXT); // weak role testRole("aria_mark", ROLE_MARK); @@ -96,12 +90,6 @@ testRole("aria_menu_mixed", ROLE_MENUPOPUP); testRole("aria_menubar", ROLE_MENUBAR); testRole("aria_menubar_mixed", ROLE_MENUBAR); - testRole("aria_menuitem", ROLE_MENUITEM); - testRole("aria_menuitem_mixed", ROLE_MENUITEM); - testRole("aria_menuitemcheckbox", ROLE_CHECK_MENU_ITEM); - testRole("aria_menuitemcheckbox_mixed", ROLE_CHECK_MENU_ITEM); - testRole("aria_menuitemradio", ROLE_RADIO_MENU_ITEM); - testRole("aria_menuitemradio_mixed", ROLE_RADIO_MENU_ITEM); testRole("aria_meter", ROLE_METER); testRole("aria_meter_mixed", ROLE_METER); testRole("aria_note", ROLE_NOTE); @@ -130,10 +118,6 @@ testRole("aria_region_as_table_with_caption_mixed", ROLE_REGION); testRole("aria_region_as_table_with_miscaption", ROLE_TABLE); testRole("aria_region_as_table_with_miscaption_mixed", ROLE_TABLE); - testRole("aria_row", ROLE_ROW); - testRole("aria_row_mixed", ROLE_ROW); - testRole("aria_rowheader", ROLE_ROWHEADER); - testRole("aria_rowheader_mixed", ROLE_ROWHEADER); testRole("aria_scrollbar", ROLE_SCROLLBAR); testRole("aria_scrollbar_mixed", ROLE_SCROLLBAR); testRole("aria_searchbox", ROLE_ENTRY); @@ -154,8 +138,6 @@ testRole("aria_superscript_mixed", ROLE_SUPERSCRIPT); testRole("aria_switch", ROLE_SWITCH); testRole("aria_switch_mixed", ROLE_SWITCH); - testRole("aria_tab", ROLE_PAGETAB); - testRole("aria_tab_mixed", ROLE_PAGETAB); testRole("aria_tablist", ROLE_PAGETABLIST); testRole("aria_tablist_mixed", ROLE_PAGETABLIST); testRole("aria_tabpanel", ROLE_PROPERTYPAGE); @@ -174,8 +156,6 @@ testRole("aria_tree_mixed", ROLE_OUTLINE); testRole("aria_treegrid", ROLE_TREE_TABLE); testRole("aria_treegrid_mixed", ROLE_TREE_TABLE); - testRole("aria_treeitem", ROLE_OUTLINEITEM); - testRole("aria_treeitem_mixed", ROLE_OUTLINEITEM); // Note: // The phrase "weak foo" here means that there is no good foo-to-platform @@ -262,6 +242,29 @@ testRole("implicit_gridcell_mixed", ROLE_GRID_CELL); // //////////////////////////////////////////////////////////////////////// + // child roles dependent on ancestor role presence + testRole("aria_columnheader", ROLE_COLUMNHEADER); + testRole("aria_columnheader_mixed", ROLE_COLUMNHEADER); + testRole("aria_gridcell", ROLE_GRID_CELL); + testRole("aria_gridcell_mixed", ROLE_GRID_CELL); + testRole("aria_rowheader", ROLE_ROWHEADER); + testRole("aria_rowheader_mixed", ROLE_ROWHEADER); + testRole("aria_listitem", ROLE_LISTITEM); + testRole("aria_listitem_mixed", ROLE_LISTITEM); + testRole("aria_menuitem", ROLE_MENUITEM); + testRole("aria_menuitem_mixed", ROLE_MENUITEM); + testRole("aria_menuitemcheckbox", ROLE_CHECK_MENU_ITEM); + testRole("aria_menuitemcheckbox_mixed", ROLE_CHECK_MENU_ITEM); + testRole("aria_menuitemradio", ROLE_RADIO_MENU_ITEM); + testRole("aria_menuitemradio_mixed", ROLE_RADIO_MENU_ITEM); + testRole("aria_row", ROLE_ROW); + testRole("aria_row_mixed", ROLE_ROW); + testRole("aria_tab", ROLE_PAGETAB); + testRole("aria_tab_mixed", ROLE_PAGETAB); + testRole("aria_treeitem", ROLE_OUTLINEITEM); + testRole("aria_treeitem_mixed", ROLE_OUTLINEITEM); + + // //////////////////////////////////////////////////////////////////////// // ignore unknown roles, take first known testRole("unknown_roles", ROLE_PUSHBUTTON); testRole("unknown_roles_mixed", ROLE_PUSHBUTTON); @@ -400,8 +403,6 @@ <span id="aria_checkbox_mixed" role="cHECKBOx"></span> <span id="aria_code" role="code"></span> <span id="aria_code_mixed" role="cODe"></span> - <span id="aria_columnheader" role="columnheader"></span> - <span id="aria_columnheader_mixed" role="cOLUMNHEADEr"></span> <span id="aria_combobox" role="combobox"></span> <span id="aria_combobox_mixed" role="cOMBOBOx"></span> <span id="aria_comment" role="comment"></span> @@ -424,8 +425,6 @@ <span id="aria_figure_mixed" role="fIGURe"></span> <span id="aria_grid" role="grid"></span> <span id="aria_grid_mixed" role="gRId"></span> - <span id="aria_gridcell" role="gridcell"></span> - <span id="aria_gridcell_mixed" role="gRIDCELl"></span> <span id="aria_group" role="group"></span> <span id="aria_group_mixed" role="gROUp"></span> <span id="aria_heading" role="heading"></span> @@ -440,8 +439,6 @@ <span id="aria_list_mixed" role="lISt"></span> <span id="aria_listbox" role="listbox"></span> <span id="aria_listbox_mixed" role="lISTBOx"></span> - <span id="aria_listitem" role="listitem"></span> - <span id="aria_listitem_mixed" role="lISTITEm"></span> <span id="aria_log" role="log"></span> <span id="aria_log_mixed" role="lOg"></span> <span id="aria_mark" role="mark"></span> @@ -454,12 +451,6 @@ <span id="aria_menu_mixed" role="mENu"></span> <span id="aria_menubar" role="menubar"></span> <span id="aria_menubar_mixed" role="mENUBAr"></span> - <span id="aria_menuitem" role="menuitem"></span> - <span id="aria_menuitem_mixed" role="mENUITEm"></span> - <span id="aria_menuitemcheckbox" role="menuitemcheckbox"></span> - <span id="aria_menuitemcheckbox_mixed" role="mENUITEMCHECKBOx"></span> - <span id="aria_menuitemradio" role="menuitemradio"></span> - <span id="aria_menuitemradio_mixed" role="mENUITEMRADIo"></span> <span id="aria_meter" role="meter"></span> <span id="aria_meter_mixed" role="meTer"></span> <span id="aria_note" role="note"></span> @@ -488,10 +479,6 @@ <table id="aria_region_as_table_with_caption_mixed" role="rEGIOn"><caption>hello</caption></table> <table id="aria_region_as_table_with_miscaption" role="region"><caption role="option">hello</caption></table> <table id="aria_region_as_table_with_miscaption_mixed" role="rEGIOn"><caption role="option">hello</caption></table> - <span id="aria_row" role="row"></span> - <span id="aria_row_mixed" role="rOw"></span> - <span id="aria_rowheader" role="rowheader"></span> - <span id="aria_rowheader_mixed" role="rOWHEADEr"></span> <span id="aria_scrollbar" role="scrollbar"></span> <span id="aria_scrollbar_mixed" role="sCROLLBAr"></span> <span id="aria_searchbox" role="textbox"></span> @@ -512,8 +499,6 @@ <span id="aria_superscript_mixed" role="sUPERSCRIPt"></span> <span id="aria_switch" role="switch"></span> <span id="aria_switch_mixed" role="sWITCh"></span> - <span id="aria_tab" role="tab"></span> - <span id="aria_tab_mixed" role="tAb"></span> <span id="aria_tablist" role="tablist"></span> <span id="aria_tablist_mixed" role="tABLISt"></span> <span id="aria_tabpanel" role="tabpanel"></span> @@ -532,8 +517,6 @@ <span id="aria_tree_mixed" role="tREe"></span> <span id="aria_treegrid" role="treegrid"></span> <span id="aria_treegrid_mixed" role="tREEGRId"></span> - <span id="aria_treeitem" role="treeitem"></span> - <span id="aria_treeitem_mixed" role="tREEITEm"></span> <article id="articlemain" role="main">a main area</article> <article id="articlemain_mixed" role="mAIn">a main area</article> @@ -665,6 +648,42 @@ </tr> </table> + <!-- child roles dependent on ancestor role presence --> + <div role="grid"> + <div role="row"> + <span id="aria_columnheader" role="columnheader"></span> + <span id="aria_columnheader_mixed" role="cOLUMNHEADEr"></span> + <span id="aria_gridcell" role="gridcell"></span> + <span id="aria_gridcell_mixed" role="gRIDCELl"></span> + <span id="aria_rowheader" role="rowheader"></span> + <span id="aria_rowheader_mixed" role="rOWHEADEr"></span> + </div> + </div> + <div role="list"> + <span id="aria_listitem" role="listitem"></span> + <span id="aria_listitem_mixed" role="lISTITEm"></span> + </div> + <div role="menu"> + <span id="aria_menuitem" role="menuitem"></span> + <span id="aria_menuitem_mixed" role="mENUITEm"></span> + <span id="aria_menuitemcheckbox" role="menuitemcheckbox"></span> + <span id="aria_menuitemcheckbox_mixed" role="mENUITEMCHECKBOx"></span> + <span id="aria_menuitemradio" role="menuitemradio"></span> + <span id="aria_menuitemradio_mixed" role="mENUITEMRADIo"></span> + </div> + <div role="table"> + <span id="aria_row" role="row"></span> + <span id="aria_row_mixed" role="rOw"></span> + </div> + <div role="tablist"> + <span id="aria_tab" role="tab"></span> + <span id="aria_tab_mixed" role="tAb"></span> + </div> + <div role="tree"> + <span id="aria_treeitem" role="treeitem"></span> + <span id="aria_treeitem_mixed" role="tREEITEm"></span> + </div> + <!-- roles transformed by ARIA state attributes --> <button aria-pressed="true" id="togglebutton"></button> <button aria-pressed="tRUe" id="togglebutton_mixed"></button> diff --git a/accessible/tests/mochitest/test_custom_element_accessibility_defaults.html b/accessible/tests/mochitest/test_custom_element_accessibility_defaults.html index 672b5c36ad..799aefc163 100644 --- a/accessible/tests/mochitest/test_custom_element_accessibility_defaults.html +++ b/accessible/tests/mochitest/test_custom_element_accessibility_defaults.html @@ -33,7 +33,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1665151 ["disabled", "button", internals => internals.ariaDisabled = "true"], ["expanded", "button", internals => internals.ariaExpanded = "true"], ["haspopup", "button", internals => internals.ariaHasPopup = "true"], - ["hidden", "region", internals => internals.ariaHidden = "false"], + ["hidden", "region", internals => internals.ariaHidden = "true"], ["invalid", "textbox", internals => internals.ariaInvalid = "true"], ["keyshortcuts", "button", internals => internals.ariaKeyShortcuts = "Alt+Shift+A"], ["label", "button", internals => internals.ariaLabel = "Default"], @@ -122,8 +122,8 @@ async function runTest() { testAttrs("default-haspopup", {"haspopup": "true"}, true); testAbsentAttrs("custom-haspopup", {"haspopup": "false"}); - ok(isAccessible("default-hidden"), "Accessible for not aria-hidden"); - ok(!isAccessible("custom-hidden"), "No accessible for aria-hidden"); + ok(!isAccessible("default-hidden"), "Accessible for aria-hidden default"); + ok(isAccessible("custom-hidden"), "Accessible for custom aria-hidden=false"); testStates("default-invalid", STATE_INVALID); testStates("custom-invalid", 0, 0, STATE_INVALID); @@ -277,7 +277,7 @@ addA11yLoadEvent(runTest); <custom-haspopup id="custom-haspopup" aria-haspopup="false"></custom-haspopup> <custom-hidden id="default-hidden"></custom-hidden> -<custom-hidden id="custom-hidden" aria-hidden="true"></custom-hidden> +<custom-hidden id="custom-hidden" aria-hidden="false"></custom-hidden> <custom-invalid id="default-invalid"></custom-invalid> <custom-invalid id="custom-invalid" aria-invalid="false"></custom-invalid> @@ -306,8 +306,10 @@ addA11yLoadEvent(runTest); <custom-orientation id="default-orientation"></custom-orientation> <custom-orientation id="custom-orientation" aria-orientation="horizontal"></custom-orientation> -<custom-posinset id="default-posinset"></custom-posinset> -<custom-posinset id="custom-posinset" aria-posinset="2"></custom-posinset> +<div role="listbox"> + <custom-posinset id="default-posinset"></custom-posinset> + <custom-posinset id="custom-posinset" aria-posinset="2"></custom-posinset> +</div> <custom-pressed id="default-pressed"></custom-pressed> <custom-pressed id="custom-pressed" aria-pressed="false"></custom-pressed> @@ -345,10 +347,10 @@ addA11yLoadEvent(runTest); <custom-selected id="default-selected"></custom-selected> <custom-selected id="custom-selected" aria-selected="false"></custom-selected> -<div role="listbox"> +<div role="list"> <custom-setsize id="default-setsize"></custom-setsize> </div> -<div role="listbox"> +<div role="list"> <custom-setsize id="custom-setsize" aria-setsize="2"></custom-setsize> <div role="listitem" aria-setsize="2"></div> </div> diff --git a/accessible/tests/mochitest/tree/test_aria_display_contents.html b/accessible/tests/mochitest/tree/test_aria_display_contents.html index 454e4e46e1..5b2331e730 100644 --- a/accessible/tests/mochitest/tree/test_aria_display_contents.html +++ b/accessible/tests/mochitest/tree/test_aria_display_contents.html @@ -53,11 +53,11 @@ ] }, ] }, { SECTION: [ - { LISTITEM: [ + { SECTION: [ { TEXT_LEAF: [ ] } ] }, ] }, - { LISTITEM: [ + { SECTION: [ { TEXT_LEAF: [ ] } ] }, ] }; @@ -93,63 +93,63 @@ </pre> <div id="gridWithoutDisplayContents" role="grid"> - <div role="row">
- <div role="columnheader">col1</div>
- <div role="columnheader">col2</div>
- </div>
- <div role="row">
- <div role="rowheader">row1</div>
- <div role="gridcell">cell1</div>
+ <div role="row"> + <div role="columnheader">col1</div> + <div role="columnheader">col2</div> + </div> + <div role="row"> + <div role="rowheader">row1</div> + <div role="gridcell">cell1</div> </div> </div> <div id="gridWithDisplayContents" role="grid" style="display:contents;"> - <div role="row">
- <div role="columnheader">col1</div>
- <div role="columnheader">col2</div>
- </div>
- <div role="row">
- <div role="rowheader">row1</div>
- <div role="gridcell">cell1</div>
+ <div role="row"> + <div role="columnheader">col1</div> + <div role="columnheader">col2</div> + </div> + <div role="row"> + <div role="rowheader">row1</div> + <div role="gridcell">cell1</div> </div> </div> <div id="gridWithDisplayContentsRow" role="grid"> - <div role="row" style="display:contents;">
- <div role="columnheader">col1</div>
- <div role="columnheader">col2</div>
- </div>
- <div role="row">
- <div role="rowheader">row1</div>
- <div role="gridcell">cell1</div>
+ <div role="row" style="display:contents;"> + <div role="columnheader">col1</div> + <div role="columnheader">col2</div> + </div> + <div role="row"> + <div role="rowheader">row1</div> + <div role="gridcell">cell1</div> </div> </div> <div id="gridWithDisplayContentsColHeader" role="grid"> - <div role="row">
- <div role="columnheader" style="display:contents;">col1</div>
- <div role="columnheader">col2</div>
- </div>
- <div role="row">
- <div role="rowheader">row1</div>
- <div role="gridcell">cell1</div>
+ <div role="row"> + <div role="columnheader" style="display:contents;">col1</div> + <div role="columnheader">col2</div> + </div> + <div role="row"> + <div role="rowheader">row1</div> + <div role="gridcell">cell1</div> </div> </div> <div id="gridWithDisplayContentsRowHeader" role="grid"> - <div role="row">
- <div role="columnheader">col1</div>
- <div role="columnheader">col2</div>
- </div>
- <div role="row">
- <div role="rowheader" style="display:contents;">row1</div>
- <div role="gridcell">cell1</div>
+ <div role="row"> + <div role="columnheader">col1</div> + <div role="columnheader">col2</div> + </div> + <div role="row"> + <div role="rowheader" style="display:contents;">row1</div> + <div role="gridcell">cell1</div> </div> </div> <div id="gridWithDisplayContentsGridCell" role="grid"> - <div role="row">
- <div role="columnheader">col1</div>
- <div role="columnheader">col2</div>
- </div>
- <div role="row">
- <div role="rowheader">row1</div>
- <div role="gridcell" style="display:contents;">cell1</div>
+ <div role="row"> + <div role="columnheader">col1</div> + <div role="columnheader">col2</div> + </div> + <div role="row"> + <div role="rowheader">row1</div> + <div role="gridcell" style="display:contents;">cell1</div> </div> </div> diff --git a/accessible/tests/mochitest/tree/test_aria_grid.html b/accessible/tests/mochitest/tree/test_aria_grid.html index 4dd30e4183..d409c87c34 100644 --- a/accessible/tests/mochitest/tree/test_aria_grid.html +++ b/accessible/tests/mochitest/tree/test_aria_grid.html @@ -19,7 +19,7 @@ var accTree = { GRID: [ - { GROUPING: [ + { ROWGROUP: [ { ROW: [ { GRID_CELL: [ { TEXT_LEAF: [ ] }, diff --git a/accessible/tests/mochitest/tree/test_aria_table.html b/accessible/tests/mochitest/tree/test_aria_table.html index 22375faf59..2d4a0f00ae 100644 --- a/accessible/tests/mochitest/tree/test_aria_table.html +++ b/accessible/tests/mochitest/tree/test_aria_table.html @@ -19,7 +19,7 @@ var accTree = { TABLE: [ - { GROUPING: [ + { ROWGROUP: [ { ROW: [ { CELL: [ { TEXT_LEAF: [ ] }, diff --git a/accessible/tests/mochitest/tree/test_display_contents.html b/accessible/tests/mochitest/tree/test_display_contents.html index 8393a35b41..9573a845d3 100644 --- a/accessible/tests/mochitest/tree/test_display_contents.html +++ b/accessible/tests/mochitest/tree/test_display_contents.html @@ -41,7 +41,7 @@ function doTest() { tree = { TABLE: [ - { GROUPING : [ + { ROWGROUP : [ { ROW: [ { CELL: [{ TEXT_LEAF: [] } ] }, { CELL: [{ TEXT_LEAF: [] } ] }, diff --git a/accessible/tests/mochitest/tree/test_table.html b/accessible/tests/mochitest/tree/test_table.html index 5f34c12067..7f7f4fe6db 100644 --- a/accessible/tests/mochitest/tree/test_table.html +++ b/accessible/tests/mochitest/tree/test_table.html @@ -283,7 +283,7 @@ // The tbody should get a grouping accessible. accTree = { TABLE: [ - { GROUPING: [ + { ROWGROUP: [ { ROW: [ { CELL: [ { TEXT_LEAF: [ ] }, diff --git a/accessible/windows/ia2/ia2Accessible.cpp b/accessible/windows/ia2/ia2Accessible.cpp index 7318dd5e6f..5bc421d086 100644 --- a/accessible/windows/ia2/ia2Accessible.cpp +++ b/accessible/windows/ia2/ia2Accessible.cpp @@ -156,7 +156,8 @@ ia2Accessible::role(long* aRole) { if (!acc) return CO_E_OBJNOTCONNECTED; #define ROLE(_geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::_geckoRole: \ *aRole = ia2Role; \ break; diff --git a/accessible/windows/msaa/MsaaAccessible.cpp b/accessible/windows/msaa/MsaaAccessible.cpp index 3a0e8fc726..56e8cfd058 100644 --- a/accessible/windows/msaa/MsaaAccessible.cpp +++ b/accessible/windows/msaa/MsaaAccessible.cpp @@ -14,6 +14,7 @@ #include "mozilla/a11y/AccessibleWrap.h" #include "mozilla/a11y/Compatibility.h" #include "mozilla/a11y/DocAccessibleParent.h" +#include "mozilla/StaticPrefs_accessibility.h" #include "MsaaAccessible.h" #include "MsaaDocAccessible.h" #include "MsaaRootAccessible.h" @@ -522,9 +523,11 @@ MsaaAccessible::QueryInterface(REFIID iid, void** ppv) { if (SUCCEEDED(hr)) { return hr; } - hr = uiaRawElmProvider::QueryInterface(iid, ppv); - if (SUCCEEDED(hr)) { - return hr; + if (StaticPrefs::accessibility_uia_enable()) { + hr = uiaRawElmProvider::QueryInterface(iid, ppv); + if (SUCCEEDED(hr)) { + return hr; + } } } if (*ppv) { @@ -769,7 +772,8 @@ MsaaAccessible::get_accRole( uint32_t msaaRole = 0; #define ROLE(_geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ - _msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \ + _msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ case roles::_geckoRole: \ msaaRole = _msaaRole; \ break; diff --git a/accessible/windows/msaa/MsaaAccessible.h b/accessible/windows/msaa/MsaaAccessible.h index 6d6582f010..fa43c441bf 100644 --- a/accessible/windows/msaa/MsaaAccessible.h +++ b/accessible/windows/msaa/MsaaAccessible.h @@ -31,7 +31,7 @@ class MsaaAccessible : public ia2Accessible, public: static MsaaAccessible* Create(Accessible* aAcc); - Accessible* Acc() { return mAcc; } + Accessible* Acc() const { return mAcc; } AccessibleWrap* LocalAcc(); uint32_t GetExistingID() const { return mID; } @@ -146,6 +146,10 @@ class MsaaAccessible : public ia2Accessible, EXCEPINFO* pExcepInfo, UINT* puArgErr) override; + // UIA's IInvokeProvider has a method called Invoke too, but it's fine because + // it accepts very different parameters. + using uiaRawElmProvider::Invoke; + protected: explicit MsaaAccessible(Accessible* aAcc); virtual ~MsaaAccessible(); diff --git a/accessible/windows/msaa/MsaaRootAccessible.cpp b/accessible/windows/msaa/MsaaRootAccessible.cpp index ac747ff3d1..2b28edb9b5 100644 --- a/accessible/windows/msaa/MsaaRootAccessible.cpp +++ b/accessible/windows/msaa/MsaaRootAccessible.cpp @@ -5,6 +5,7 @@ #include "mozilla/a11y/DocAccessibleParent.h" #include "mozilla/dom/BrowserParent.h" +#include "mozilla/StaticPrefs_accessibility.h" #include "mozilla/WindowsVersion.h" #include "MsaaRootAccessible.h" #include "Relation.h" @@ -36,6 +37,13 @@ MsaaRootAccessible::InternalQueryInterface(REFIID aIid, void** aOutInterface) { return S_OK; } + if (StaticPrefs::accessibility_uia_enable() && + aIid == IID_IRawElementProviderFragmentRoot) { + RefPtr<IRawElementProviderFragmentRoot> root = this; + root.forget(aOutInterface); + return S_OK; + } + // ...Otherwise we pass through to the base COM implementation of // QueryInterface which is provided by MsaaDocAccessible. return MsaaDocAccessible::QueryInterface(aIid, aOutInterface); diff --git a/accessible/windows/msaa/MsaaRootAccessible.h b/accessible/windows/msaa/MsaaRootAccessible.h index a51533d7ba..efc42bc094 100644 --- a/accessible/windows/msaa/MsaaRootAccessible.h +++ b/accessible/windows/msaa/MsaaRootAccessible.h @@ -8,12 +8,14 @@ #include "mozilla/mscom/Aggregation.h" #include "MsaaDocAccessible.h" +#include "UiaRoot.h" namespace mozilla { namespace a11y { +class RootAccessible; -class MsaaRootAccessible : public MsaaDocAccessible { +class MsaaRootAccessible : public MsaaDocAccessible, public UiaRoot { public: explicit MsaaRootAccessible(Accessible* aAcc) : MsaaDocAccessible(aAcc), mOuter(&mInternalUnknown) {} diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp index 018042c5d3..f4d1c7b176 100644 --- a/accessible/windows/msaa/Platform.cpp +++ b/accessible/windows/msaa/Platform.cpp @@ -69,10 +69,13 @@ void a11y::ProxyDestroyed(RemoteAccessible* aProxy) { void a11y::PlatformEvent(Accessible* aTarget, uint32_t aEventType) { MsaaAccessible::FireWinEvent(aTarget, aEventType); + uiaRawElmProvider::RaiseUiaEventForGeckoEvent(aTarget, aEventType); } -void a11y::PlatformStateChangeEvent(Accessible* aTarget, uint64_t, bool) { +void a11y::PlatformStateChangeEvent(Accessible* aTarget, uint64_t aState, + bool aEnabled) { MsaaAccessible::FireWinEvent(aTarget, nsIAccessibleEvent::EVENT_STATE_CHANGE); + uiaRawElmProvider::RaiseUiaEventForStateChange(aTarget, aState, aEnabled); } void a11y::PlatformFocusEvent(Accessible* aTarget, @@ -91,6 +94,8 @@ void a11y::PlatformFocusEvent(Accessible* aTarget, AccessibleWrap::UpdateSystemCaretFor(aTarget, aCaretRect); MsaaAccessible::FireWinEvent(aTarget, nsIAccessibleEvent::EVENT_FOCUS); + uiaRawElmProvider::RaiseUiaEventForGeckoEvent( + aTarget, nsIAccessibleEvent::EVENT_FOCUS); } void a11y::PlatformCaretMoveEvent(Accessible* aTarget, int32_t aOffset, diff --git a/accessible/windows/uia/UiaRoot.cpp b/accessible/windows/uia/UiaRoot.cpp new file mode 100644 index 0000000000..0e8c86ce6a --- /dev/null +++ b/accessible/windows/uia/UiaRoot.cpp @@ -0,0 +1,61 @@ +/* -*- 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 "UiaRoot.h" + +#include "MsaaRootAccessible.h" +#include "RootAccessible.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +// UiaRoot + +Accessible* UiaRoot::Acc() { + auto* mr = static_cast<MsaaRootAccessible*>(this); + return static_cast<MsaaAccessible*>(mr)->Acc(); +} + +// IRawElementProviderFragmentRoot + +STDMETHODIMP +UiaRoot::ElementProviderFromPoint( + double aX, double aY, + __RPC__deref_out_opt IRawElementProviderFragment** aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + *aRetVal = nullptr; + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + if (Accessible* target = acc->ChildAtPoint( + aX, aY, Accessible::EWhichChildAtPoint::DeepestChild)) { + RefPtr<IRawElementProviderFragment> fragment = + MsaaAccessible::GetFrom(target); + fragment.forget(aRetVal); + } + return S_OK; +} + +STDMETHODIMP +UiaRoot::GetFocus(__RPC__deref_out_opt IRawElementProviderFragment** aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + *aRetVal = nullptr; + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + if (Accessible* focus = acc->FocusedChild()) { + RefPtr<IRawElementProviderFragment> fragment = + MsaaAccessible::GetFrom(focus); + fragment.forget(aRetVal); + } + return S_OK; +} diff --git a/accessible/windows/uia/UiaRoot.h b/accessible/windows/uia/UiaRoot.h new file mode 100644 index 0000000000..91d1c00b3d --- /dev/null +++ b/accessible/windows/uia/UiaRoot.h @@ -0,0 +1,40 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_UiaRoot_h__ +#define mozilla_a11y_UiaRoot_h__ + +#include "objbase.h" +#include "uiautomation.h" + +namespace mozilla { +namespace a11y { +class Accessible; + +/** + * IRawElementProviderFragmentRoot implementation. + */ +class UiaRoot : public IRawElementProviderFragmentRoot { + public: + // IRawElementProviderFragmentRoot + virtual HRESULT STDMETHODCALLTYPE ElementProviderFromPoint( + /* [in] */ double aX, + /* [in] */ double aY, + /* [retval][out] */ + __RPC__deref_out_opt IRawElementProviderFragment** aRetVal); + + virtual HRESULT STDMETHODCALLTYPE GetFocus( + /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderFragment** + aRetVal); + + private: + Accessible* Acc(); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/uia/moz.build b/accessible/windows/uia/moz.build index 058aacc579..c52a24d612 100644 --- a/accessible/windows/uia/moz.build +++ b/accessible/windows/uia/moz.build @@ -6,6 +6,7 @@ SOURCES += [ "uiaRawElmProvider.cpp", + "UiaRoot.cpp", ] LOCAL_INCLUDES += [ diff --git a/accessible/windows/uia/uiaRawElmProvider.cpp b/accessible/windows/uia/uiaRawElmProvider.cpp index 881ed22277..c022e40cef 100644 --- a/accessible/windows/uia/uiaRawElmProvider.cpp +++ b/accessible/windows/uia/uiaRawElmProvider.cpp @@ -6,39 +6,174 @@ #include "uiaRawElmProvider.h" +#include <comdef.h> +#include <uiautomationcoreapi.h> + #include "AccAttributes.h" #include "AccessibleWrap.h" #include "ARIAMap.h" #include "LocalAccessible-inl.h" #include "mozilla/a11y/RemoteAccessible.h" +#include "mozilla/StaticPrefs_accessibility.h" #include "MsaaAccessible.h" +#include "MsaaRootAccessible.h" +#include "nsAccessibilityService.h" +#include "nsAccUtils.h" #include "nsTextEquivUtils.h" +#include "RootAccessible.h" using namespace mozilla; using namespace mozilla::a11y; +// Helper functions + +static ToggleState ToToggleState(uint64_t aState) { + if (aState & states::MIXED) { + return ToggleState_Indeterminate; + } + if (aState & (states::CHECKED | states::PRESSED)) { + return ToggleState_On; + } + return ToggleState_Off; +} + +static ExpandCollapseState ToExpandCollapseState(uint64_t aState) { + if (aState & states::EXPANDED) { + return ExpandCollapseState_Expanded; + } + // If aria-haspopup is specified without aria-expanded, we should still expose + // collapsed, since aria-haspopup infers that it can be expanded. The + // alternative is ExpandCollapseState_LeafNode, but that means that the + // element can't be expanded nor collapsed. + if (aState & (states::COLLAPSED | states::HASPOPUP)) { + return ExpandCollapseState_Collapsed; + } + return ExpandCollapseState_LeafNode; +} + //////////////////////////////////////////////////////////////////////////////// // uiaRawElmProvider //////////////////////////////////////////////////////////////////////////////// -Accessible* uiaRawElmProvider::Acc() { - return static_cast<MsaaAccessible*>(this)->Acc(); +Accessible* uiaRawElmProvider::Acc() const { + return static_cast<const MsaaAccessible*>(this)->Acc(); } -// IUnknown - -// Because uiaRawElmProvider inherits multiple COM interfaces (and thus multiple -// IUnknowns), we need to explicitly implement AddRef and Release to make -// our QueryInterface implementation (IMPL_IUNKNOWN2) happy. -ULONG STDMETHODCALLTYPE uiaRawElmProvider::AddRef() { - return static_cast<MsaaAccessible*>(this)->AddRef(); +/* static */ +void uiaRawElmProvider::RaiseUiaEventForGeckoEvent(Accessible* aAcc, + uint32_t aGeckoEvent) { + if (!StaticPrefs::accessibility_uia_enable()) { + return; + } + auto* uia = MsaaAccessible::GetFrom(aAcc); + if (!uia) { + return; + } + PROPERTYID property = 0; + _variant_t newVal; + bool gotNewVal = false; + // For control pattern properties, we can't use GetPropertyValue. In those + // cases, we must set newVal appropriately and set gotNewVal to true. + switch (aGeckoEvent) { + case nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE: + property = UIA_FullDescriptionPropertyId; + break; + case nsIAccessibleEvent::EVENT_FOCUS: + ::UiaRaiseAutomationEvent(uia, UIA_AutomationFocusChangedEventId); + return; + case nsIAccessibleEvent::EVENT_NAME_CHANGE: + property = UIA_NamePropertyId; + break; + case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE: + property = UIA_ValueValuePropertyId; + newVal.vt = VT_BSTR; + uia->get_Value(&newVal.bstrVal); + gotNewVal = true; + break; + } + if (property && ::UiaClientsAreListening()) { + // We can't get the old value. Thankfully, clients don't seem to need it. + _variant_t oldVal; + if (!gotNewVal) { + // This isn't a pattern property, so we can use GetPropertyValue. + uia->GetPropertyValue(property, &newVal); + } + ::UiaRaiseAutomationPropertyChangedEvent(uia, property, oldVal, newVal); + } } -ULONG STDMETHODCALLTYPE uiaRawElmProvider::Release() { - return static_cast<MsaaAccessible*>(this)->Release(); +/* static */ +void uiaRawElmProvider::RaiseUiaEventForStateChange(Accessible* aAcc, + uint64_t aState, + bool aEnabled) { + if (!StaticPrefs::accessibility_uia_enable()) { + return; + } + auto* uia = MsaaAccessible::GetFrom(aAcc); + if (!uia) { + return; + } + PROPERTYID property = 0; + _variant_t newVal; + switch (aState) { + case states::CHECKED: + case states::MIXED: + case states::PRESSED: + property = UIA_ToggleToggleStatePropertyId; + newVal.vt = VT_I4; + newVal.lVal = ToToggleState(aEnabled ? aState : 0); + break; + case states::COLLAPSED: + case states::EXPANDED: + case states::HASPOPUP: + property = UIA_ExpandCollapseExpandCollapseStatePropertyId; + newVal.vt = VT_I4; + newVal.lVal = ToExpandCollapseState(aEnabled ? aState : 0); + break; + case states::UNAVAILABLE: + property = UIA_IsEnabledPropertyId; + newVal.vt = VT_BOOL; + newVal.boolVal = aEnabled ? VARIANT_FALSE : VARIANT_TRUE; + break; + default: + return; + } + MOZ_ASSERT(property); + if (::UiaClientsAreListening()) { + // We can't get the old value. Thankfully, clients don't seem to need it. + _variant_t oldVal; + ::UiaRaiseAutomationPropertyChangedEvent(uia, property, oldVal, newVal); + } } -IMPL_IUNKNOWN2(uiaRawElmProvider, IAccessibleEx, IRawElementProviderSimple) +// IUnknown + +STDMETHODIMP +uiaRawElmProvider::QueryInterface(REFIID aIid, void** aInterface) { + *aInterface = nullptr; + if (aIid == IID_IAccessibleEx) { + *aInterface = static_cast<IAccessibleEx*>(this); + } else if (aIid == IID_IRawElementProviderSimple) { + *aInterface = static_cast<IRawElementProviderSimple*>(this); + } else if (aIid == IID_IRawElementProviderFragment) { + *aInterface = static_cast<IRawElementProviderFragment*>(this); + } else if (aIid == IID_IExpandCollapseProvider) { + *aInterface = static_cast<IExpandCollapseProvider*>(this); + } else if (aIid == IID_IInvokeProvider) { + *aInterface = static_cast<IInvokeProvider*>(this); + } else if (aIid == IID_IScrollItemProvider) { + *aInterface = static_cast<IScrollItemProvider*>(this); + } else if (aIid == IID_IToggleProvider) { + *aInterface = static_cast<IToggleProvider*>(this); + } else if (aIid == IID_IValueProvider) { + *aInterface = static_cast<IValueProvider*>(this); + } else { + return E_NOINTERFACE; + } + MOZ_ASSERT(*aInterface); + static_cast<MsaaAccessible*>(this)->AddRef(); + return S_OK; +} //////////////////////////////////////////////////////////////////////////////// // IAccessibleEx @@ -113,8 +248,9 @@ uiaRawElmProvider::get_ProviderOptions( __RPC__out enum ProviderOptions* aOptions) { if (!aOptions) return E_INVALIDARG; - // This method is not used with IAccessibleEx implementations. - *aOptions = ProviderOptions_ServerSideProvider; + *aOptions = static_cast<enum ProviderOptions>( + ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading | + ProviderOptions_HasNativeIAccessible); return S_OK; } @@ -122,8 +258,46 @@ STDMETHODIMP uiaRawElmProvider::GetPatternProvider( PATTERNID aPatternId, __RPC__deref_out_opt IUnknown** aPatternProvider) { if (!aPatternProvider) return E_INVALIDARG; - *aPatternProvider = nullptr; + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + switch (aPatternId) { + case UIA_ExpandCollapsePatternId: + if (HasExpandCollapsePattern()) { + RefPtr<IExpandCollapseProvider> expand = this; + expand.forget(aPatternProvider); + } + return S_OK; + case UIA_InvokePatternId: + // Per the UIA documentation, we should only expose the Invoke pattern "if + // the same behavior is not exposed through another control pattern + // provider". + if (acc->ActionCount() > 0 && !HasTogglePattern() && + !HasExpandCollapsePattern()) { + RefPtr<IInvokeProvider> invoke = this; + invoke.forget(aPatternProvider); + } + return S_OK; + case UIA_ScrollItemPatternId: { + RefPtr<IScrollItemProvider> scroll = this; + scroll.forget(aPatternProvider); + return S_OK; + } + case UIA_TogglePatternId: + if (HasTogglePattern()) { + RefPtr<IToggleProvider> toggle = this; + toggle.forget(aPatternProvider); + } + return S_OK; + case UIA_ValuePatternId: + if (HasValuePattern()) { + RefPtr<IValueProvider> value = this; + value.forget(aPatternProvider); + } + return S_OK; + } return S_OK; } @@ -230,11 +404,71 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId, break; } - case UIA_IsControlElementPropertyId: + case UIA_AutomationIdPropertyId: { + nsAutoString id; + acc->DOMNodeID(id); + if (!id.IsEmpty()) { + aPropertyValue->vt = VT_BSTR; + aPropertyValue->bstrVal = ::SysAllocString(id.get()); + return S_OK; + } + break; + } + + case UIA_ControlTypePropertyId: + aPropertyValue->vt = VT_I4; + aPropertyValue->lVal = GetControlType(); + break; + + case UIA_FullDescriptionPropertyId: { + nsAutoString desc; + acc->Description(desc); + if (!desc.IsEmpty()) { + aPropertyValue->vt = VT_BSTR; + aPropertyValue->bstrVal = ::SysAllocString(desc.get()); + return S_OK; + } + break; + } + + case UIA_HasKeyboardFocusPropertyId: + aPropertyValue->vt = VT_BOOL; + aPropertyValue->boolVal = VARIANT_FALSE; + if (auto* focusMgr = FocusMgr()) { + if (focusMgr->IsFocused(acc)) { + aPropertyValue->boolVal = VARIANT_TRUE; + } + } + return S_OK; + case UIA_IsContentElementPropertyId: + case UIA_IsControlElementPropertyId: aPropertyValue->vt = VT_BOOL; aPropertyValue->boolVal = IsControl() ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; + + case UIA_IsEnabledPropertyId: + aPropertyValue->vt = VT_BOOL; + aPropertyValue->boolVal = + (acc->State() & states::UNAVAILABLE) ? VARIANT_FALSE : VARIANT_TRUE; + return S_OK; + + case UIA_IsKeyboardFocusablePropertyId: + aPropertyValue->vt = VT_BOOL; + aPropertyValue->boolVal = + (acc->State() & states::FOCUSABLE) ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; + + case UIA_NamePropertyId: { + nsAutoString name; + acc->Name(name); + if (!name.IsEmpty()) { + aPropertyValue->vt = VT_BSTR; + aPropertyValue->bstrVal = ::SysAllocString(name.get()); + return S_OK; + } + break; + } } return S_OK; @@ -244,9 +478,274 @@ STDMETHODIMP uiaRawElmProvider::get_HostRawElementProvider( __RPC__deref_out_opt IRawElementProviderSimple** aRawElmProvider) { if (!aRawElmProvider) return E_INVALIDARG; - - // This method is not used with IAccessibleEx implementations. *aRawElmProvider = nullptr; + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + if (acc->IsRoot()) { + HWND hwnd = MsaaAccessible::GetHWNDFor(acc); + return UiaHostProviderFromHwnd(hwnd, aRawElmProvider); + } + return S_OK; +} + +// IRawElementProviderFragment + +STDMETHODIMP +uiaRawElmProvider::Navigate( + enum NavigateDirection aDirection, + __RPC__deref_out_opt IRawElementProviderFragment** aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + *aRetVal = nullptr; + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + Accessible* target = nullptr; + switch (aDirection) { + case NavigateDirection_Parent: + if (!acc->IsRoot()) { + target = acc->Parent(); + } + break; + case NavigateDirection_NextSibling: + if (!acc->IsRoot()) { + target = acc->NextSibling(); + } + break; + case NavigateDirection_PreviousSibling: + if (!acc->IsRoot()) { + target = acc->PrevSibling(); + } + break; + case NavigateDirection_FirstChild: + if (!nsAccUtils::MustPrune(acc)) { + target = acc->FirstChild(); + } + break; + case NavigateDirection_LastChild: + if (!nsAccUtils::MustPrune(acc)) { + target = acc->LastChild(); + } + break; + default: + return E_INVALIDARG; + } + RefPtr<IRawElementProviderFragment> fragment = + MsaaAccessible::GetFrom(target); + fragment.forget(aRetVal); + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::get_BoundingRectangle(__RPC__out struct UiaRect* aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + LayoutDeviceIntRect rect = acc->Bounds(); + aRetVal->left = rect.X(); + aRetVal->top = rect.Y(); + aRetVal->width = rect.Width(); + aRetVal->height = rect.Height(); + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::GetEmbeddedFragmentRoots( + __RPC__deref_out_opt SAFEARRAY** aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + *aRetVal = nullptr; + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::SetFocus() { + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + acc->TakeFocus(); + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::get_FragmentRoot( + __RPC__deref_out_opt IRawElementProviderFragmentRoot** aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + *aRetVal = nullptr; + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + LocalAccessible* localAcc = acc->AsLocal(); + if (!localAcc) { + localAcc = acc->AsRemote()->OuterDocOfRemoteBrowser(); + if (!localAcc) { + return CO_E_OBJNOTCONNECTED; + } + } + MsaaAccessible* msaa = MsaaAccessible::GetFrom(localAcc->RootAccessible()); + RefPtr<IRawElementProviderFragmentRoot> fragRoot = + static_cast<MsaaRootAccessible*>(msaa); + fragRoot.forget(aRetVal); + return S_OK; +} + +// IInvokeProvider methods + +STDMETHODIMP +uiaRawElmProvider::Invoke() { + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + if (acc->DoAction(0)) { + // We don't currently have a way to notify when the action was actually + // handled. The UIA documentation says it's okay to fire this immediately if + // it "is not possible or practical to wait until the action is complete". + ::UiaRaiseAutomationEvent(this, UIA_Invoke_InvokedEventId); + } + return S_OK; +} + +// IToggleProvider methods + +STDMETHODIMP +uiaRawElmProvider::Toggle() { + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + acc->DoAction(0); + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::get_ToggleState(__RPC__out enum ToggleState* aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + *aRetVal = ToToggleState(acc->State()); + return S_OK; +} + +// IExpandCollapseProvider methods + +STDMETHODIMP +uiaRawElmProvider::Expand() { + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + if (acc->State() & states::EXPANDED) { + return UIA_E_INVALIDOPERATION; + } + acc->DoAction(0); + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::Collapse() { + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + if (acc->State() & states::COLLAPSED) { + return UIA_E_INVALIDOPERATION; + } + acc->DoAction(0); + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::get_ExpandCollapseState( + __RPC__out enum ExpandCollapseState* aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + *aRetVal = ToExpandCollapseState(acc->State()); + return S_OK; +} + +// IScrollItemProvider methods + +MOZ_CAN_RUN_SCRIPT_BOUNDARY STDMETHODIMP uiaRawElmProvider::ScrollIntoView() { + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + acc->ScrollTo(nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE); + return S_OK; +} + +// IValueProvider methods + +STDMETHODIMP +uiaRawElmProvider::SetValue(__RPC__in LPCWSTR aVal) { + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + HyperTextAccessibleBase* ht = acc->AsHyperTextBase(); + if (!ht || !acc->IsTextRole()) { + return UIA_E_INVALIDOPERATION; + } + if (acc->State() & (states::READONLY | states::UNAVAILABLE)) { + return UIA_E_INVALIDOPERATION; + } + nsAutoString text(aVal); + ht->ReplaceText(text); + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::get_Value(__RPC__deref_out_opt BSTR* aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + *aRetVal = nullptr; + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + nsAutoString value; + acc->Value(value); + *aRetVal = ::SysAllocStringLen(value.get(), value.Length()); + if (!*aRetVal) { + return E_OUTOFMEMORY; + } + return S_OK; +} + +STDMETHODIMP +uiaRawElmProvider::get_IsReadOnly(__RPC__out BOOL* aRetVal) { + if (!aRetVal) { + return E_INVALIDARG; + } + Accessible* acc = Acc(); + if (!acc) { + return CO_E_OBJNOTCONNECTED; + } + *aRetVal = acc->State() & states::READONLY; return S_OK; } @@ -314,3 +813,44 @@ bool uiaRawElmProvider::IsControl() { return true; } + +long uiaRawElmProvider::GetControlType() const { + Accessible* acc = Acc(); + MOZ_ASSERT(acc); +#define ROLE(_geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ + msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \ + nameRule) \ + case roles::_geckoRole: \ + return uiaControlType; \ + break; + switch (acc->Role()) { +#include "RoleMap.h" + } +#undef ROLE + MOZ_CRASH("Unknown role."); + return 0; +} + +bool uiaRawElmProvider::HasTogglePattern() { + Accessible* acc = Acc(); + MOZ_ASSERT(acc); + return acc->State() & states::CHECKABLE || + acc->Role() == roles::TOGGLE_BUTTON; +} + +bool uiaRawElmProvider::HasExpandCollapsePattern() { + Accessible* acc = Acc(); + MOZ_ASSERT(acc); + return acc->State() & (states::EXPANDABLE | states::HASPOPUP); +} + +bool uiaRawElmProvider::HasValuePattern() const { + Accessible* acc = Acc(); + MOZ_ASSERT(acc); + if (acc->HasNumericValue() || acc->IsCombobox() || acc->IsHTMLLink() || + acc->IsTextField()) { + return true; + } + const nsRoleMapEntry* roleMapEntry = acc->ARIARoleMap(); + return roleMapEntry && roleMapEntry->Is(nsGkAtoms::textbox); +} diff --git a/accessible/windows/uia/uiaRawElmProvider.h b/accessible/windows/uia/uiaRawElmProvider.h index 0e5172c805..0e05d1a030 100644 --- a/accessible/windows/uia/uiaRawElmProvider.h +++ b/accessible/windows/uia/uiaRawElmProvider.h @@ -7,9 +7,9 @@ #ifndef mozilla_a11y_uiaRawElmProvider_h__ #define mozilla_a11y_uiaRawElmProvider_h__ -#include "objbase.h" -#include "IUnknownImpl.h" -#include "uiautomation.h" +#include <objbase.h> +#include <stdint.h> +#include <uiautomation.h> namespace mozilla { namespace a11y { @@ -20,12 +20,21 @@ class Accessible; * IRawElementProviderSimple implementation (maintains IAccessibleEx approach). */ class uiaRawElmProvider : public IAccessibleEx, - public IRawElementProviderSimple { + public IRawElementProviderSimple, + public IRawElementProviderFragment, + public IInvokeProvider, + public IToggleProvider, + public IExpandCollapseProvider, + public IScrollItemProvider, + public IValueProvider { public: + static void RaiseUiaEventForGeckoEvent(Accessible* aAcc, + uint32_t aGeckoEvent); + static void RaiseUiaEventForStateChange(Accessible* aAcc, uint64_t aState, + bool aEnabled); + // IUnknown - DECL_IUNKNOWN_INHERITED - ULONG STDMETHODCALLTYPE AddRef() override; - ULONG STDMETHODCALLTYPE Release() override; + STDMETHODIMP QueryInterface(REFIID aIid, void** aInterface); // IAccessibleEx virtual HRESULT STDMETHODCALLTYPE GetObjectForChild( @@ -59,9 +68,63 @@ class uiaRawElmProvider : public IAccessibleEx, /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderSimple** aRawElmProvider); + // IRawElementProviderFragment + virtual HRESULT STDMETHODCALLTYPE Navigate( + /* [in] */ enum NavigateDirection aDirection, + /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderFragment** + aRetVal); + + // GetRuntimeId is shared with IAccessibleEx. + + virtual HRESULT STDMETHODCALLTYPE get_BoundingRectangle( + /* [retval][out] */ __RPC__out struct UiaRect* aRetVal); + + virtual HRESULT STDMETHODCALLTYPE GetEmbeddedFragmentRoots( + /* [retval][out] */ __RPC__deref_out_opt SAFEARRAY** aRetVal); + + virtual HRESULT STDMETHODCALLTYPE SetFocus(void); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_FragmentRoot( + /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderFragmentRoot** + aRetVal); + + // IInvokeProvider + virtual HRESULT STDMETHODCALLTYPE Invoke(void); + + // IToggleProvider + virtual HRESULT STDMETHODCALLTYPE Toggle(void); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ToggleState( + /* [retval][out] */ __RPC__out enum ToggleState* aRetVal); + + // IExpandCollapseProvider + virtual HRESULT STDMETHODCALLTYPE Expand(void); + + virtual HRESULT STDMETHODCALLTYPE Collapse(void); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ExpandCollapseState( + /* [retval][out] */ __RPC__out enum ExpandCollapseState* aRetVal); + + // IScrollItemProvider + virtual HRESULT STDMETHODCALLTYPE ScrollIntoView(void); + + // IValueProvider + virtual HRESULT STDMETHODCALLTYPE SetValue( + /* [in] */ __RPC__in LPCWSTR val); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Value( + /* [retval][out] */ __RPC__deref_out_opt BSTR* pRetVal); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsReadOnly( + /* [retval][out] */ __RPC__out BOOL* pRetVal); + private: - Accessible* Acc(); + Accessible* Acc() const; bool IsControl(); + long GetControlType() const; + bool HasTogglePattern(); + bool HasExpandCollapsePattern(); + bool HasValuePattern() const; }; } // namespace a11y diff --git a/accessible/xpcom/xpcAccessible.cpp b/accessible/xpcom/xpcAccessible.cpp index 37901e3ec2..ebf7ba8e80 100644 --- a/accessible/xpcom/xpcAccessible.cpp +++ b/accessible/xpcom/xpcAccessible.cpp @@ -636,7 +636,8 @@ xpcAccessible::ScrollToPoint(uint32_t aCoordinateType, int32_t aX, int32_t aY) { NS_IMETHODIMP xpcAccessible::Announce(const nsAString& aAnnouncement, uint16_t aPriority) { - if (RemoteAccessible* proxy = IntlGeneric()->AsRemote()) { + RemoteAccessible* proxy = IntlGeneric()->AsRemote(); + if (proxy) { #if defined(XP_WIN) return NS_ERROR_NOT_IMPLEMENTED; #else |