/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "XULTabAccessible.h" #include "ARIAMap.h" #include "nsAccUtils.h" #include "Relation.h" #include "mozilla/a11y/Role.h" #include "States.h" // NOTE: alphabetically ordered #include "mozilla/dom/Document.h" #include "nsIDOMXULSelectCntrlItemEl.h" #include "nsIDOMXULRelatedElement.h" #include "nsXULElement.h" #include "mozilla/dom/BindingDeclarations.h" using namespace mozilla::a11y; //////////////////////////////////////////////////////////////////////////////// // XULTabAccessible //////////////////////////////////////////////////////////////////////////////// XULTabAccessible::XULTabAccessible(nsIContent* aContent, DocAccessible* aDoc) : HyperTextAccessible(aContent, aDoc) {} //////////////////////////////////////////////////////////////////////////////// // XULTabAccessible: LocalAccessible bool XULTabAccessible::HasPrimaryAction() const { return true; } void XULTabAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) { if (aIndex == eAction_Switch) aName.AssignLiteral("switch"); } bool XULTabAccessible::DoAction(uint8_t index) const { if (index == eAction_Switch) { // XXXbz Could this just FromContent? RefPtr tab = nsXULElement::FromNodeOrNull(mContent); if (tab) { tab->Click(mozilla::dom::CallerType::System); return true; } } return false; } //////////////////////////////////////////////////////////////////////////////// // XULTabAccessible: LocalAccessible role XULTabAccessible::NativeRole() const { return roles::PAGETAB; } uint64_t XULTabAccessible::NativeState() const { // Possible states: focused, focusable, unavailable(disabled), offscreen. // get focus and disable status from base class uint64_t state = AccessibleWrap::NativeState(); // Check whether the tab is selected and/or pinned nsCOMPtr tab = Elm()->AsXULSelectControlItem(); if (tab) { bool selected = false; if (NS_SUCCEEDED(tab->GetSelected(&selected)) && selected) { state |= states::SELECTED; } if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::pinned)) { state |= states::PINNED; } } return state; } uint64_t XULTabAccessible::NativeInteractiveState() const { uint64_t state = LocalAccessible::NativeInteractiveState(); return (state & states::UNAVAILABLE) ? state : state | states::SELECTABLE; } Relation XULTabAccessible::RelationByType(RelationType aType) const { Relation rel = AccessibleWrap::RelationByType(aType); if (aType != RelationType::LABEL_FOR) return rel; // Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible. ErrorResult rv; nsIContent* parent = mContent->AsElement()->Closest("tabs"_ns, rv); if (!parent) return rel; nsCOMPtr tabsElm = parent->AsElement()->AsXULRelated(); if (!tabsElm) return rel; RefPtr tabpanelElement; tabsElm->GetRelatedElement(GetNode(), getter_AddRefs(tabpanelElement)); if (!tabpanelElement) return rel; rel.AppendTarget(mDoc, tabpanelElement); return rel; } void XULTabAccessible::ApplyARIAState(uint64_t* aState) const { HyperTextAccessible::ApplyARIAState(aState); // XUL tab has an implicit ARIA role of tab, so support aria-selected. // Don't use aria::MapToState because that will set the SELECTABLE state // even if the tab is disabled. if (nsAccUtils::IsARIASelected(this)) { *aState |= states::SELECTED; } } //////////////////////////////////////////////////////////////////////////////// // XULTabsAccessible //////////////////////////////////////////////////////////////////////////////// XULTabsAccessible::XULTabsAccessible(nsIContent* aContent, DocAccessible* aDoc) : XULSelectControlAccessible(aContent, aDoc) {} role XULTabsAccessible::NativeRole() const { return roles::PAGETABLIST; } bool XULTabsAccessible::HasPrimaryAction() const { return false; } void XULTabsAccessible::Value(nsString& aValue) const { aValue.Truncate(); } ENameValueFlag XULTabsAccessible::NativeName(nsString& aName) const { // no name return eNameOK; } void XULTabsAccessible::ApplyARIAState(uint64_t* aState) const { XULSelectControlAccessible::ApplyARIAState(aState); // XUL tabs has an implicit ARIA role of tablist, so support // aria-multiselectable. MOZ_ASSERT(Elm()); aria::MapToState(aria::eARIAMultiSelectable, Elm(), aState); } // XUL tabs is a single selection control and doesn't allow ARIA selection. // However, if aria-multiselectable is used, it becomes a multiselectable // control, where both native and ARIA markup are used to set selection. // Therefore, if aria-multiselectable is set, use the base implementation of // the selection retrieval methods in order to support ARIA selection. // We don't bother overriding the selection setting methods because // current front-end code using XUL tabs doesn't support setting of // aria-selected by the a11y engine and we still want to be able to set the // primary selected item according to XUL. void XULTabsAccessible::SelectedItems(nsTArray* aItems) { if (nsAccUtils::IsARIAMultiSelectable(this)) { AccessibleWrap::SelectedItems(aItems); } else { XULSelectControlAccessible::SelectedItems(aItems); } } Accessible* XULTabsAccessible::GetSelectedItem(uint32_t aIndex) { if (nsAccUtils::IsARIAMultiSelectable(this)) { return AccessibleWrap::GetSelectedItem(aIndex); } return XULSelectControlAccessible::GetSelectedItem(aIndex); } uint32_t XULTabsAccessible::SelectedItemCount() { if (nsAccUtils::IsARIAMultiSelectable(this)) { return AccessibleWrap::SelectedItemCount(); } return XULSelectControlAccessible::SelectedItemCount(); } bool XULTabsAccessible::IsItemSelected(uint32_t aIndex) { if (nsAccUtils::IsARIAMultiSelectable(this)) { return AccessibleWrap::IsItemSelected(aIndex); } return XULSelectControlAccessible::IsItemSelected(aIndex); } //////////////////////////////////////////////////////////////////////////////// // XULTabpanelsAccessible //////////////////////////////////////////////////////////////////////////////// role XULTabpanelsAccessible::NativeRole() const { return roles::PANE; } //////////////////////////////////////////////////////////////////////////////// // XULTabpanelAccessible //////////////////////////////////////////////////////////////////////////////// XULTabpanelAccessible::XULTabpanelAccessible(nsIContent* aContent, DocAccessible* aDoc) : AccessibleWrap(aContent, aDoc) {} role XULTabpanelAccessible::NativeRole() const { return roles::PROPERTYPAGE; } Relation XULTabpanelAccessible::RelationByType(RelationType aType) const { Relation rel = AccessibleWrap::RelationByType(aType); if (aType != RelationType::LABELLED_BY) return rel; // Expose 'LABELLED_BY' relation on tabpanel accessible for tab accessible. if (!mContent->GetParent()) return rel; nsCOMPtr tabpanelsElm = mContent->GetParent()->AsElement()->AsXULRelated(); if (!tabpanelsElm) return rel; RefPtr tabElement; tabpanelsElm->GetRelatedElement(GetNode(), getter_AddRefs(tabElement)); if (!tabElement) return rel; rel.AppendTarget(mDoc, tabElement); return rel; }