diff options
Diffstat (limited to 'accessible/xul')
25 files changed, 6945 insertions, 0 deletions
diff --git a/accessible/xul/XULAlertAccessible.cpp b/accessible/xul/XULAlertAccessible.cpp new file mode 100644 index 000000000..4b1b5bd8d --- /dev/null +++ b/accessible/xul/XULAlertAccessible.cpp @@ -0,0 +1,68 @@ +/* -*- 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 "XULAlertAccessible.h" + +#include "Accessible-inl.h" +#include "Role.h" +#include "States.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULAlertAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULAlertAccessible:: + XULAlertAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ + mGenericTypes |= eAlert; +} + +XULAlertAccessible::~XULAlertAccessible() +{ +} + +NS_IMPL_ISUPPORTS_INHERITED0(XULAlertAccessible, Accessible) + +role +XULAlertAccessible::NativeRole() +{ + return roles::ALERT; +} + +uint64_t +XULAlertAccessible::NativeState() +{ + return Accessible::NativeState() | states::ALERT; +} + +ENameValueFlag +XULAlertAccessible::Name(nsString& aName) +{ + // Screen readers need to read contents of alert, not the accessible name. + // If we have both some screen readers will read the alert twice. + aName.Truncate(); + return eNameOK; +} + +//////////////////////////////////////////////////////////////////////////////// +// Widgets + +bool +XULAlertAccessible::IsWidget() const +{ + return true; +} + +Accessible* +XULAlertAccessible::ContainerWidget() const +{ + // If a part of colorpicker widget. + if (mParent && mParent->IsMenuButton()) + return mParent; + return nullptr; +} diff --git a/accessible/xul/XULAlertAccessible.h b/accessible/xul/XULAlertAccessible.h new file mode 100644 index 000000000..62bf4cb46 --- /dev/null +++ b/accessible/xul/XULAlertAccessible.h @@ -0,0 +1,41 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_XULAlertAccessible_h__ +#define mozilla_a11y_XULAlertAccessible_h__ + +#include "AccessibleWrap.h" + +namespace mozilla { +namespace a11y { + +/** + * Accessible for supporting XUL alerts. + */ + +class XULAlertAccessible : public AccessibleWrap +{ +public: + XULAlertAccessible(nsIContent* aContent, DocAccessible* aDoc); + + NS_DECL_ISUPPORTS_INHERITED + + // Accessible + virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // Widgets + virtual bool IsWidget() const override; + virtual Accessible* ContainerWidget() const override; + +protected: + ~XULAlertAccessible(); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xul/XULColorPickerAccessible.cpp b/accessible/xul/XULColorPickerAccessible.cpp new file mode 100644 index 000000000..1bc507e9a --- /dev/null +++ b/accessible/xul/XULColorPickerAccessible.cpp @@ -0,0 +1,143 @@ +/* -*- 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 "XULColorPickerAccessible.h" + +#include "Accessible-inl.h" +#include "nsAccUtils.h" +#include "nsCoreUtils.h" +#include "DocAccessible.h" +#include "Role.h" +#include "States.h" + +#include "nsIDOMElement.h" +#include "nsMenuPopupFrame.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULColorPickerTileAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULColorPickerTileAccessible:: + XULColorPickerTileAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULColorPickerTileAccessible: Accessible + +void +XULColorPickerTileAccessible::Value(nsString& aValue) +{ + aValue.Truncate(); + + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::color, aValue); +} + +role +XULColorPickerTileAccessible::NativeRole() +{ + return roles::PUSHBUTTON; +} + +uint64_t +XULColorPickerTileAccessible::NativeState() +{ + uint64_t state = AccessibleWrap::NativeState(); + if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::selected)) + state |= states::SELECTED; + + return state; +} + +uint64_t +XULColorPickerTileAccessible::NativeInteractiveState() const +{ + return NativelyUnavailable() ? + states::UNAVAILABLE : states::FOCUSABLE | states::SELECTABLE; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULColorPickerTileAccessible: Widgets + +Accessible* +XULColorPickerTileAccessible::ContainerWidget() const +{ + Accessible* parent = Parent(); + if (parent) { + Accessible* grandParent = parent->Parent(); + if (grandParent && grandParent->IsMenuButton()) + return grandParent; + } + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULColorPickerAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULColorPickerAccessible:: + XULColorPickerAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULColorPickerTileAccessible(aContent, aDoc) +{ + mGenericTypes |= eMenuButton; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULColorPickerAccessible: Accessible + +uint64_t +XULColorPickerAccessible::NativeState() +{ + uint64_t state = AccessibleWrap::NativeState(); + return state | states::HASPOPUP; +} + +role +XULColorPickerAccessible::NativeRole() +{ + return roles::BUTTONDROPDOWNGRID; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULColorPickerAccessible: Widgets + +bool +XULColorPickerAccessible::IsWidget() const +{ + return true; +} + +bool +XULColorPickerAccessible::IsActiveWidget() const +{ + return FocusMgr()->HasDOMFocus(mContent); +} + +bool +XULColorPickerAccessible::AreItemsOperable() const +{ + Accessible* menuPopup = mChildren.SafeElementAt(0, nullptr); + if (menuPopup) { + nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame()); + return menuPopupFrame && menuPopupFrame->IsOpen(); + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULColorPickerAccessible: Accessible + +bool +XULColorPickerAccessible::IsAcceptableChild(nsIContent* aEl) const +{ + nsAutoString role; + nsCoreUtils::XBLBindingRole(aEl, role); + return role.EqualsLiteral("xul:panel") && + aEl->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus, + nsGkAtoms::_true, eCaseMatters); +} diff --git a/accessible/xul/XULColorPickerAccessible.h b/accessible/xul/XULColorPickerAccessible.h new file mode 100644 index 000000000..fba4111ca --- /dev/null +++ b/accessible/xul/XULColorPickerAccessible.h @@ -0,0 +1,57 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_XULColorPickerAccessible_h__ +#define mozilla_a11y_XULColorPickerAccessible_h__ + +#include "AccessibleWrap.h" + +namespace mozilla { +namespace a11y { + +/** + * Used for color button in colorpicker palette. + */ +class XULColorPickerTileAccessible : public AccessibleWrap +{ +public: + XULColorPickerTileAccessible(nsIContent* aContent, + DocAccessible* aDoc); + + // Accessible + virtual void Value(nsString& aValue) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + virtual uint64_t NativeInteractiveState() const override; + + // Widgets + virtual Accessible* ContainerWidget() const override; +}; + + +/** + * Used for colorpicker button (xul:colorpicker@type="button"). + */ +class XULColorPickerAccessible : public XULColorPickerTileAccessible +{ +public: + XULColorPickerAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // Widgets + virtual bool IsWidget() const override; + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; + + virtual bool IsAcceptableChild(nsIContent* aEl) const override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xul/XULComboboxAccessible.cpp b/accessible/xul/XULComboboxAccessible.cpp new file mode 100644 index 000000000..1bca6d58e --- /dev/null +++ b/accessible/xul/XULComboboxAccessible.cpp @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; 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 "XULComboboxAccessible.h" + +#include "Accessible-inl.h" +#include "nsAccessibilityService.h" +#include "DocAccessible.h" +#include "nsCoreUtils.h" +#include "Role.h" +#include "States.h" + +#include "nsIAutoCompleteInput.h" +#include "nsIDOMXULMenuListElement.h" +#include "nsIDOMXULSelectCntrlItemEl.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULComboboxAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULComboboxAccessible:: + XULComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + nsGkAtoms::autocomplete, eIgnoreCase)) + mGenericTypes |= eAutoComplete; + else + mGenericTypes |= eCombobox; + + // Both the XUL <textbox type="autocomplete"> and <menulist editable="true"> + // widgets use XULComboboxAccessible. We need to walk the anonymous children + // for these so that the entry field is a child. Otherwise no XBL children. + if (!mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) && + !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable, + nsGkAtoms::_true, eIgnoreCase)) { + mStateFlags |= eNoXBLKids; + } +} + +role +XULComboboxAccessible::NativeRole() +{ + return IsAutoComplete() ? roles::AUTOCOMPLETE : roles::COMBOBOX; +} + +uint64_t +XULComboboxAccessible::NativeState() +{ + // As a nsComboboxAccessible we can have the following states: + // STATE_FOCUSED + // STATE_FOCUSABLE + // STATE_HASPOPUP + // STATE_EXPANDED + // STATE_COLLAPSED + + // Get focus status from base class + uint64_t state = Accessible::NativeState(); + + nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent)); + if (menuList) { + bool isOpen = false; + menuList->GetOpen(&isOpen); + if (isOpen) + state |= states::EXPANDED; + else + state |= states::COLLAPSED; + } + + return state | states::HASPOPUP; +} + +void +XULComboboxAccessible::Description(nsString& aDescription) +{ + aDescription.Truncate(); + // Use description of currently focused option + nsCOMPtr<nsIDOMXULMenuListElement> menuListElm(do_QueryInterface(mContent)); + if (!menuListElm) + return; + + nsCOMPtr<nsIDOMXULSelectControlItemElement> focusedOptionItem; + menuListElm->GetSelectedItem(getter_AddRefs(focusedOptionItem)); + nsCOMPtr<nsIContent> focusedOptionContent = + do_QueryInterface(focusedOptionItem); + if (focusedOptionContent && mDoc) { + Accessible* focusedOptionAcc = mDoc->GetAccessible(focusedOptionContent); + if (focusedOptionAcc) + focusedOptionAcc->Description(aDescription); + } +} + +void +XULComboboxAccessible::Value(nsString& aValue) +{ + aValue.Truncate(); + + // The value is the option or text shown entered in the combobox. + nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent)); + if (menuList) + menuList->GetLabel(aValue); +} + +uint8_t +XULComboboxAccessible::ActionCount() +{ + // Just one action (click). + return 1; +} + +bool +XULComboboxAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != XULComboboxAccessible::eAction_Click) + return false; + + // Programmaticaly toggle the combo box. + nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent)); + if (!menuList) + return false; + + bool isDroppedDown = false; + menuList->GetOpen(&isDroppedDown); + menuList->SetOpen(!isDroppedDown); + return true; +} + +void +XULComboboxAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + aName.Truncate(); + if (aIndex != XULComboboxAccessible::eAction_Click) + return; + + nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent)); + if (!menuList) + return; + + bool isDroppedDown = false; + menuList->GetOpen(&isDroppedDown); + if (isDroppedDown) + aName.AssignLiteral("close"); + else + aName.AssignLiteral("open"); +} + +//////////////////////////////////////////////////////////////////////////////// +// Widgets + +bool +XULComboboxAccessible::IsActiveWidget() const +{ + if (IsAutoComplete() || + mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable, + nsGkAtoms::_true, eIgnoreCase)) { + int32_t childCount = mChildren.Length(); + for (int32_t idx = 0; idx < childCount; idx++) { + Accessible* child = mChildren[idx]; + if (child->Role() == roles::ENTRY) + return FocusMgr()->HasDOMFocus(child->GetContent()); + } + return false; + } + + return FocusMgr()->HasDOMFocus(mContent); +} + +bool +XULComboboxAccessible::AreItemsOperable() const +{ + if (IsAutoComplete()) { + nsCOMPtr<nsIAutoCompleteInput> autoCompleteInputElm = + do_QueryInterface(mContent); + if (autoCompleteInputElm) { + bool isOpen = false; + autoCompleteInputElm->GetPopupOpen(&isOpen); + return isOpen; + } + return false; + } + + nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = do_QueryInterface(mContent); + if (menuListElm) { + bool isOpen = false; + menuListElm->GetOpen(&isOpen); + return isOpen; + } + + return false; +} diff --git a/accessible/xul/XULComboboxAccessible.h b/accessible/xul/XULComboboxAccessible.h new file mode 100644 index 000000000..679f479b5 --- /dev/null +++ b/accessible/xul/XULComboboxAccessible.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; 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/. */ + +#ifndef mozilla_a11y_XULComboboxAccessible_h__ +#define mozilla_a11y_XULComboboxAccessible_h__ + +#include "XULMenuAccessible.h" + +namespace mozilla { +namespace a11y { + +/** + * Used for XUL comboboxes like xul:menulist and autocomplete textbox. + */ +class XULComboboxAccessible : public AccessibleWrap +{ +public: + enum { eAction_Click = 0 }; + + XULComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual void Description(nsString& aDescription) override; + virtual void Value(nsString& aValue) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + + // Widgets + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xul/XULElementAccessibles.cpp b/accessible/xul/XULElementAccessibles.cpp new file mode 100644 index 000000000..b50ddb4ed --- /dev/null +++ b/accessible/xul/XULElementAccessibles.cpp @@ -0,0 +1,288 @@ +/* -*- 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 "XULElementAccessibles.h" + +#include "Accessible-inl.h" +#include "BaseAccessibles.h" +#include "DocAccessible-inl.h" +#include "nsAccUtils.h" +#include "nsCoreUtils.h" +#include "nsTextEquivUtils.h" +#include "Relation.h" +#include "Role.h" +#include "States.h" +#include "TextUpdater.h" + +#ifdef A11Y_LOG +#include "Logging.h" +#endif + +#include "nsIDOMXULDescriptionElement.h" +#include "nsNameSpaceManager.h" +#include "nsNetUtil.h" +#include "nsString.h" +#include "nsTextBoxFrame.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULLabelAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULLabelAccessible:: + XULLabelAccessible(nsIContent* aContent, DocAccessible* aDoc) : + HyperTextAccessibleWrap(aContent, aDoc) +{ + mType = eXULLabelType; + + // If @value attribute is given then it's rendered instead text content. In + // this case we need to create a text leaf accessible to make @value attribute + // accessible. + // XXX: text interface doesn't let you get the text by words. + nsTextBoxFrame* textBoxFrame = do_QueryFrame(mContent->GetPrimaryFrame()); + if (textBoxFrame) { + mValueTextLeaf = new XULLabelTextLeafAccessible(mContent, mDoc); + mDoc->BindToDocument(mValueTextLeaf, nullptr); + + nsAutoString text; + textBoxFrame->GetCroppedTitle(text); + mValueTextLeaf->SetText(text); + AppendChild(mValueTextLeaf); + } +} + +void +XULLabelAccessible::Shutdown() +{ + mValueTextLeaf = nullptr; + HyperTextAccessibleWrap::Shutdown(); +} + +ENameValueFlag +XULLabelAccessible::NativeName(nsString& aName) +{ + // if the value attr doesn't exist, the screen reader must get the accessible text + // from the accessible text interface or from the children + if (mValueTextLeaf) + return mValueTextLeaf->Name(aName); + + return Accessible::NativeName(aName); +} + +role +XULLabelAccessible::NativeRole() +{ + return roles::LABEL; +} + +uint64_t +XULLabelAccessible::NativeState() +{ + // Labels and description have read only state + // They are not focusable or selectable + return HyperTextAccessibleWrap::NativeState() | states::READONLY; +} + +Relation +XULLabelAccessible::RelationByType(RelationType aType) +{ + Relation rel = HyperTextAccessibleWrap::RelationByType(aType); + if (aType == RelationType::LABEL_FOR) { + // Caption is the label for groupbox + nsIContent* parent = mContent->GetFlattenedTreeParent(); + if (parent && parent->IsXULElement(nsGkAtoms::caption)) { + Accessible* parent = Parent(); + if (parent && parent->Role() == roles::GROUPING) + rel.AppendTarget(parent); + } + } + + return rel; +} + +void +XULLabelAccessible::UpdateLabelValue(const nsString& aValue) +{ +#ifdef A11Y_LOG + if (logging::IsEnabled(logging::eText)) { + logging::MsgBegin("TEXT", "text may be changed (xul:label @value update)"); + logging::Node("container", mContent); + logging::MsgEntry("old text '%s'", + NS_ConvertUTF16toUTF8(mValueTextLeaf->Text()).get()); + logging::MsgEntry("new text: '%s'", + NS_ConvertUTF16toUTF8(aValue).get()); + logging::MsgEnd(); + } +#endif + + TextUpdater::Run(mDoc, mValueTextLeaf, aValue); +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULLabelTextLeafAccessible +//////////////////////////////////////////////////////////////////////////////// + +role +XULLabelTextLeafAccessible::NativeRole() +{ + return roles::TEXT_LEAF; +} + +uint64_t +XULLabelTextLeafAccessible::NativeState() +{ + return TextLeafAccessibleWrap::NativeState() | states::READONLY; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULTooltipAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTooltipAccessible:: + XULTooltipAccessible(nsIContent* aContent, DocAccessible* aDoc) : + LeafAccessible(aContent, aDoc) +{ +} + +uint64_t +XULTooltipAccessible::NativeState() +{ + return LeafAccessible::NativeState() | states::READONLY; +} + +role +XULTooltipAccessible::NativeRole() +{ + return roles::TOOLTIP; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULLinkAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULLinkAccessible:: + XULLinkAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULLabelAccessible(aContent, aDoc) +{ +} + +XULLinkAccessible::~XULLinkAccessible() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULLinkAccessible: Accessible + +void +XULLinkAccessible::Value(nsString& aValue) +{ + aValue.Truncate(); + + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, aValue); +} + +ENameValueFlag +XULLinkAccessible::NativeName(nsString& aName) +{ + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName); + if (!aName.IsEmpty()) + return eNameOK; + + nsTextEquivUtils::GetNameFromSubtree(this, aName); + return aName.IsEmpty() ? eNameOK : eNameFromSubtree; +} + +role +XULLinkAccessible::NativeRole() +{ + return roles::LINK; +} + + +uint64_t +XULLinkAccessible::NativeLinkState() const +{ + return states::LINKED; +} + +uint8_t +XULLinkAccessible::ActionCount() +{ + return 1; +} + +void +XULLinkAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + aName.Truncate(); + + if (aIndex == eAction_Jump) + aName.AssignLiteral("jump"); +} + +bool +XULLinkAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != eAction_Jump) + return false; + + DoCommand(); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULLinkAccessible: HyperLinkAccessible + +bool +XULLinkAccessible::IsLink() +{ + // Expose HyperLinkAccessible unconditionally. + return true; +} + +uint32_t +XULLinkAccessible::StartOffset() +{ + // If XUL link accessible is not contained by hypertext accessible then + // start offset matches index in parent because the parent doesn't contains + // a text. + // XXX: accessible parent of XUL link accessible should be a hypertext + // accessible. + if (Accessible::IsLink()) + return Accessible::StartOffset(); + return IndexInParent(); +} + +uint32_t +XULLinkAccessible::EndOffset() +{ + if (Accessible::IsLink()) + return Accessible::EndOffset(); + return IndexInParent() + 1; +} + +already_AddRefed<nsIURI> +XULLinkAccessible::AnchorURIAt(uint32_t aAnchorIndex) +{ + if (aAnchorIndex != 0) + return nullptr; + + nsAutoString href; + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href); + + nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI(); + nsIDocument* document = mContent->OwnerDoc(); + + nsCOMPtr<nsIURI> anchorURI; + NS_NewURI(getter_AddRefs(anchorURI), href, + document->GetDocumentCharacterSet().get(), + baseURI); + + return anchorURI.forget(); +} diff --git a/accessible/xul/XULElementAccessibles.h b/accessible/xul/XULElementAccessibles.h new file mode 100644 index 000000000..57a5ea03b --- /dev/null +++ b/accessible/xul/XULElementAccessibles.h @@ -0,0 +1,116 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_XULElementAccessibles_h__ +#define mozilla_a11y_XULElementAccessibles_h__ + +#include "HyperTextAccessibleWrap.h" +#include "TextLeafAccessibleWrap.h" + +namespace mozilla { +namespace a11y { + +class XULLabelTextLeafAccessible; + +/** + * Used for XUL description and label elements. + */ +class XULLabelAccessible : public HyperTextAccessibleWrap +{ +public: + XULLabelAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual void Shutdown() override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + virtual Relation RelationByType(RelationType aType) override; + + void UpdateLabelValue(const nsString& aValue); + +protected: + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; + +private: + RefPtr<XULLabelTextLeafAccessible> mValueTextLeaf; +}; + +inline XULLabelAccessible* +Accessible::AsXULLabel() +{ + return IsXULLabel() ? static_cast<XULLabelAccessible*>(this) : nullptr; +} + + +/** + * Used to implement text interface on XUL label accessible in case when text + * is provided by @value attribute (no underlying text frame). + */ +class XULLabelTextLeafAccessible final : public TextLeafAccessibleWrap +{ +public: + XULLabelTextLeafAccessible(nsIContent* aContent, DocAccessible* aDoc) : + TextLeafAccessibleWrap(aContent, aDoc) + { mStateFlags |= eSharedNode; } + + virtual ~XULLabelTextLeafAccessible() { } + + // Accessible + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; +}; + + +/** + * Used for XUL tooltip element. + */ +class XULTooltipAccessible : public LeafAccessible +{ + +public: + XULTooltipAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; +}; + +class XULLinkAccessible : public XULLabelAccessible +{ + +public: + XULLinkAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual void Value(nsString& aValue) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeLinkState() const override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + + // HyperLinkAccessible + virtual bool IsLink() override; + virtual uint32_t StartOffset() override; + virtual uint32_t EndOffset() override; + virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex) override; + +protected: + virtual ~XULLinkAccessible(); + + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; + + enum { eAction_Jump = 0 }; + +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xul/XULFormControlAccessible.cpp b/accessible/xul/XULFormControlAccessible.cpp new file mode 100644 index 000000000..95947f37c --- /dev/null +++ b/accessible/xul/XULFormControlAccessible.cpp @@ -0,0 +1,633 @@ +/* -*- 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 "XULFormControlAccessible.h" + +#include "Accessible-inl.h" +#include "HTMLFormControlAccessible.h" +#include "nsAccUtils.h" +#include "nsCoreUtils.h" +#include "DocAccessible.h" +#include "nsIAccessibleRelation.h" +#include "Relation.h" +#include "Role.h" +#include "States.h" +#include "TreeWalker.h" +#include "XULMenuAccessible.h" + +#include "nsIDOMNSEditableElement.h" +#include "nsIDOMXULButtonElement.h" +#include "nsIDOMXULCheckboxElement.h" +#include "nsIDOMXULMenuListElement.h" +#include "nsIDOMXULSelectCntrlItemEl.h" +#include "nsIDOMXULTextboxElement.h" +#include "nsIEditor.h" +#include "nsIFrame.h" +#include "nsITextControlFrame.h" +#include "nsMenuPopupFrame.h" +#include "nsNameSpaceManager.h" +#include "mozilla/dom/Element.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULButtonAccessible:: + XULButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ + if (ContainsMenu()) { + mGenericTypes |= eMenuButton; + } else { + mGenericTypes |= eButton; + } +} + +XULButtonAccessible::~XULButtonAccessible() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible: nsISupports + +NS_IMPL_ISUPPORTS_INHERITED0(XULButtonAccessible, Accessible) + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible: nsIAccessible + +uint8_t +XULButtonAccessible::ActionCount() +{ + return 1; +} + +void +XULButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click) + aName.AssignLiteral("press"); +} + +bool +XULButtonAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != 0) + return false; + + DoCommand(); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible: Accessible + +role +XULButtonAccessible::NativeRole() +{ + return roles::PUSHBUTTON; +} + +uint64_t +XULButtonAccessible::NativeState() +{ + // Possible states: focused, focusable, unavailable(disabled). + + // get focus and disable status from base class + uint64_t state = Accessible::NativeState(); + + // Buttons can be checked -- they simply appear pressed in rather than checked + nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement(do_QueryInterface(mContent)); + if (xulButtonElement) { + nsAutoString type; + xulButtonElement->GetType(type); + if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) { + state |= states::CHECKABLE; + bool checked = false; + int32_t checkState = 0; + xulButtonElement->GetChecked(&checked); + if (checked) { + state |= states::PRESSED; + xulButtonElement->GetCheckState(&checkState); + if (checkState == nsIDOMXULButtonElement::CHECKSTATE_MIXED) { + state |= states::MIXED; + } + } + } + } + + if (ContainsMenu()) + state |= states::HASPOPUP; + + if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_default)) + state |= states::DEFAULT; + + return state; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible: Widgets + +bool +XULButtonAccessible::IsWidget() const +{ + return true; +} + +bool +XULButtonAccessible::IsActiveWidget() const +{ + return FocusMgr()->HasDOMFocus(mContent); +} + +bool +XULButtonAccessible::AreItemsOperable() const +{ + if (IsMenuButton()) { + Accessible* menuPopup = mChildren.SafeElementAt(0, nullptr); + if (menuPopup) { + nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame()); + return menuPopupFrame->IsOpen(); + } + } + return false; // no items +} + +Accessible* +XULButtonAccessible::ContainerWidget() const +{ + if (IsMenuButton() && mParent && mParent->IsAutoComplete()) + return mParent; + return nullptr; +} + +bool +XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const +{ + // In general XUL button has not accessible children. Nevertheless menu + // buttons can have button (@type="menu-button") and popup accessibles + // (@type="menu-button", @type="menu" or columnpicker. + + // XXX: no children until the button is menu button. Probably it's not + // totally correct but in general AT wants to have leaf buttons. + nsAutoString role; + nsCoreUtils::XBLBindingRole(aEl, role); + + // Get an accessible for menupopup or panel elements. + if (role.EqualsLiteral("xul:menupopup")) { + return true; + } + + // Button type="menu-button" contains a real button. Get an accessible + // for it. Ignore dropmarker button which is placed as a last child. + if ((!role.EqualsLiteral("xul:button") && + !role.EqualsLiteral("xul:toolbarbutton")) || + aEl->IsXULElement(nsGkAtoms::dropMarker)) { + return false; + } + + return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + nsGkAtoms::menuButton, eCaseMatters); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible protected + +bool +XULButtonAccessible::ContainsMenu() const +{ + static nsIContent::AttrValuesArray strings[] = + {&nsGkAtoms::menu, &nsGkAtoms::menuButton, nullptr}; + + return mContent->FindAttrValueIn(kNameSpaceID_None, + nsGkAtoms::type, + strings, eCaseMatters) >= 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULDropmarkerAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULDropmarkerAccessible:: + XULDropmarkerAccessible(nsIContent* aContent, DocAccessible* aDoc) : + LeafAccessible(aContent, aDoc) +{ +} + +uint8_t +XULDropmarkerAccessible::ActionCount() +{ + return 1; +} + +bool +XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const +{ + bool isOpen = false; + + nsIContent* parent = mContent->GetFlattenedTreeParent(); + + while (parent) { + nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement = + do_QueryInterface(parent); + if (parentButtonElement) { + parentButtonElement->GetOpen(&isOpen); + if (aToggleOpen) + parentButtonElement->SetOpen(!isOpen); + return isOpen; + } + + nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement = + do_QueryInterface(parent); + if (parentMenuListElement) { + parentMenuListElement->GetOpen(&isOpen); + if (aToggleOpen) + parentMenuListElement->SetOpen(!isOpen); + return isOpen; + } + parent = parent->GetFlattenedTreeParent(); + } + + return isOpen; +} + +void +XULDropmarkerAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + aName.Truncate(); + if (aIndex == eAction_Click) { + if (DropmarkerOpen(false)) + aName.AssignLiteral("close"); + else + aName.AssignLiteral("open"); + } +} + +bool +XULDropmarkerAccessible::DoAction(uint8_t index) +{ + if (index == eAction_Click) { + DropmarkerOpen(true); // Reverse the open attribute + return true; + } + return false; +} + +role +XULDropmarkerAccessible::NativeRole() +{ + return roles::PUSHBUTTON; +} + +uint64_t +XULDropmarkerAccessible::NativeState() +{ + return DropmarkerOpen(false) ? states::PRESSED : 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULCheckboxAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULCheckboxAccessible:: + XULCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc) : + LeafAccessible(aContent, aDoc) +{ +} + +role +XULCheckboxAccessible::NativeRole() +{ + return roles::CHECKBUTTON; +} + +uint8_t +XULCheckboxAccessible::ActionCount() +{ + return 1; +} + +void +XULCheckboxAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click) { + if (NativeState() & states::CHECKED) + aName.AssignLiteral("uncheck"); + else + aName.AssignLiteral("check"); + } +} + +bool +XULCheckboxAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != eAction_Click) + return false; + + DoCommand(); + return true; +} + +uint64_t +XULCheckboxAccessible::NativeState() +{ + // Possible states: focused, focusable, unavailable(disabled), checked + // Get focus and disable status from base class + uint64_t state = LeafAccessible::NativeState(); + + state |= states::CHECKABLE; + + // Determine Checked state + nsCOMPtr<nsIDOMXULCheckboxElement> xulCheckboxElement = + do_QueryInterface(mContent); + if (xulCheckboxElement) { + bool checked = false; + xulCheckboxElement->GetChecked(&checked); + if (checked) { + state |= states::CHECKED; + int32_t checkState = 0; + xulCheckboxElement->GetCheckState(&checkState); + if (checkState == nsIDOMXULCheckboxElement::CHECKSTATE_MIXED) + state |= states::MIXED; + } + } + + return state; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULGroupboxAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULGroupboxAccessible:: + XULGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +role +XULGroupboxAccessible::NativeRole() +{ + return roles::GROUPING; +} + +ENameValueFlag +XULGroupboxAccessible::NativeName(nsString& aName) +{ + // XXX: we use the first related accessible only. + Accessible* label = + RelationByType(RelationType::LABELLED_BY).Next(); + if (label) + return label->Name(aName); + + return eNameOK; +} + +Relation +XULGroupboxAccessible::RelationByType(RelationType aType) +{ + Relation rel = AccessibleWrap::RelationByType(aType); + if (aType != RelationType::LABELLED_BY) + return rel; + + // The label for xul:groupbox is generated from xul:label that is + // inside the anonymous content of the xul:caption. + // The xul:label has an accessible object but the xul:caption does not + uint32_t childCount = ChildCount(); + for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { + Accessible* childAcc = GetChildAt(childIdx); + if (childAcc->Role() == roles::LABEL) { + // Ensure that it's our label + Relation reverseRel = childAcc->RelationByType(RelationType::LABEL_FOR); + Accessible* testGroupbox = nullptr; + while ((testGroupbox = reverseRel.Next())) + if (testGroupbox == this) { + // The <label> points back to this groupbox + rel.AppendTarget(childAcc); + } + } + } + + return rel; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULRadioButtonAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULRadioButtonAccessible:: + XULRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) : + RadioButtonAccessible(aContent, aDoc) +{ +} + +uint64_t +XULRadioButtonAccessible::NativeState() +{ + uint64_t state = LeafAccessible::NativeState(); + state |= states::CHECKABLE; + + nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton = + do_QueryInterface(mContent); + if (radioButton) { + bool selected = false; // Radio buttons can be selected + radioButton->GetSelected(&selected); + if (selected) { + state |= states::CHECKED; + } + } + + return state; +} + +uint64_t +XULRadioButtonAccessible::NativeInteractiveState() const +{ + return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULRadioButtonAccessible: Widgets + +Accessible* +XULRadioButtonAccessible::ContainerWidget() const +{ + return mParent; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULRadioGroupAccessible +//////////////////////////////////////////////////////////////////////////////// + +/** + * XUL Radio Group + * The Radio Group proxies for the Radio Buttons themselves. The Group gets + * focus whereas the Buttons do not. So we only have an accessible object for + * this for the purpose of getting the proper RadioButton. Need this here to + * avoid circular reference problems when navigating the accessible tree and + * for getting to the radiobuttons. + */ + +XULRadioGroupAccessible:: + XULRadioGroupAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULSelectControlAccessible(aContent, aDoc) +{ +} + +role +XULRadioGroupAccessible::NativeRole() +{ + return roles::RADIO_GROUP; +} + +uint64_t +XULRadioGroupAccessible::NativeInteractiveState() const +{ + // The radio group is not focusable. Sometimes the focus controller will + // report that it is focused. That means that the actual selected radio button + // should be considered focused. + return NativelyUnavailable() ? states::UNAVAILABLE : 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULRadioGroupAccessible: Widgets + +bool +XULRadioGroupAccessible::IsWidget() const +{ + return true; +} + +bool +XULRadioGroupAccessible::IsActiveWidget() const +{ + return FocusMgr()->HasDOMFocus(mContent); +} + +bool +XULRadioGroupAccessible::AreItemsOperable() const +{ + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULStatusBarAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULStatusBarAccessible:: + XULStatusBarAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +role +XULStatusBarAccessible::NativeRole() +{ + return roles::STATUSBAR; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULToolbarButtonAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULToolbarButtonAccessible:: + XULToolbarButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULButtonAccessible(aContent, aDoc) +{ +} + +void +XULToolbarButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet, + int32_t* aSetSize) +{ + int32_t setSize = 0; + int32_t posInSet = 0; + + Accessible* parent = Parent(); + if (!parent) + return; + + uint32_t childCount = parent->ChildCount(); + for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { + Accessible* child = parent->GetChildAt(childIdx); + if (IsSeparator(child)) { // end of a group of buttons + if (posInSet) + break; // we've found our group, so we're done + + setSize = 0; // not our group, so start a new group + + } else { + setSize++; // another button in the group + + if (child == this) + posInSet = setSize; // we've found our button + } + } + + *aPosInSet = posInSet; + *aSetSize = setSize; +} + +bool +XULToolbarButtonAccessible::IsSeparator(Accessible* aAccessible) +{ + nsIContent* content = aAccessible->GetContent(); + return content && content->IsAnyOfXULElements(nsGkAtoms::toolbarseparator, + nsGkAtoms::toolbarspacer, + nsGkAtoms::toolbarspring); +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULToolbarAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULToolbarAccessible:: + XULToolbarAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +role +XULToolbarAccessible::NativeRole() +{ + return roles::TOOLBAR; +} + +ENameValueFlag +XULToolbarAccessible::NativeName(nsString& aName) +{ + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::toolbarname, aName)) + aName.CompressWhitespace(); + + return eNameOK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULToolbarAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULToolbarSeparatorAccessible:: + XULToolbarSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc) : + LeafAccessible(aContent, aDoc) +{ +} + +role +XULToolbarSeparatorAccessible::NativeRole() +{ + return roles::SEPARATOR; +} + +uint64_t +XULToolbarSeparatorAccessible::NativeState() +{ + return 0; +} diff --git a/accessible/xul/XULFormControlAccessible.h b/accessible/xul/XULFormControlAccessible.h new file mode 100644 index 000000000..4f11668c9 --- /dev/null +++ b/accessible/xul/XULFormControlAccessible.h @@ -0,0 +1,218 @@ +/* -*- 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/. */ + +#ifndef MOZILLA_A11Y_XULFormControlAccessible_H_ +#define MOZILLA_A11Y_XULFormControlAccessible_H_ + +// NOTE: alphabetically ordered +#include "AccessibleWrap.h" +#include "FormControlAccessible.h" +#include "HyperTextAccessibleWrap.h" +#include "XULSelectControlAccessible.h" + +namespace mozilla { +namespace a11y { + +/** + * Used for XUL progressmeter element. + */ +typedef ProgressMeterAccessible<100> XULProgressMeterAccessible; + +/** + * Used for XUL button. + * + * @note Don't inherit from LeafAccessible - it doesn't allow children + * and a button can have a dropmarker child. + */ +class XULButtonAccessible : public AccessibleWrap +{ +public: + enum { eAction_Click = 0 }; + XULButtonAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + // Accessible + virtual mozilla::a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + + // Widgets + virtual bool IsWidget() const override; + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; + virtual Accessible* ContainerWidget() const override; + + virtual bool IsAcceptableChild(nsIContent* aEl) const override; + +protected: + virtual ~XULButtonAccessible(); + + // XULButtonAccessible + bool ContainsMenu() const; +}; + + +/** + * Used for XUL checkbox element. + */ +class XULCheckboxAccessible : public LeafAccessible +{ +public: + enum { eAction_Click = 0 }; + XULCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual mozilla::a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; +}; + +/** + * Used for XUL dropmarker element. + */ +class XULDropmarkerAccessible : public LeafAccessible +{ +public: + enum { eAction_Click = 0 }; + XULDropmarkerAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual mozilla::a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + +private: + bool DropmarkerOpen(bool aToggleOpen) const; +}; + +/** + * Used for XUL groupbox element. + */ +class XULGroupboxAccessible final : public AccessibleWrap +{ +public: + XULGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual mozilla::a11y::role NativeRole() override; + virtual Relation RelationByType(RelationType aType) override; + +protected: + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; +}; + +/** + * Used for XUL radio element (radio button). + */ +class XULRadioButtonAccessible : public RadioButtonAccessible +{ + +public: + XULRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual uint64_t NativeState() override; + virtual uint64_t NativeInteractiveState() const override; + + // Widgets + virtual Accessible* ContainerWidget() const override; +}; + +/** + * Used for XUL radiogroup element. + */ +class XULRadioGroupAccessible : public XULSelectControlAccessible +{ +public: + XULRadioGroupAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual mozilla::a11y::role NativeRole() override; + virtual uint64_t NativeInteractiveState() const override; + + // Widgets + virtual bool IsWidget() const override; + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; +}; + +/** + * Used for XUL statusbar element. + */ +class XULStatusBarAccessible : public AccessibleWrap +{ +public: + XULStatusBarAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual mozilla::a11y::role NativeRole() override; +}; + +/** + * Used for XUL toolbarbutton element. + */ +class XULToolbarButtonAccessible : public XULButtonAccessible +{ +public: + XULToolbarButtonAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual void GetPositionAndSizeInternal(int32_t* aPosInSet, + int32_t* aSetSize) override; + + // nsXULToolbarButtonAccessible + static bool IsSeparator(Accessible* aAccessible); +}; + +/** + * Used for XUL toolbar element. + */ +class XULToolbarAccessible : public AccessibleWrap +{ +public: + XULToolbarAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual mozilla::a11y::role NativeRole() override; + +protected: + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; +}; + +/** + * Used for XUL toolbarseparator element. + */ +class XULToolbarSeparatorAccessible : public LeafAccessible +{ +public: + XULToolbarSeparatorAccessible(nsIContent* aContent, + DocAccessible* aDoc); + + // Accessible + virtual mozilla::a11y::role NativeRole() override; + virtual uint64_t NativeState() override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif + diff --git a/accessible/xul/XULListboxAccessible.cpp b/accessible/xul/XULListboxAccessible.cpp new file mode 100644 index 000000000..52c3ddf0f --- /dev/null +++ b/accessible/xul/XULListboxAccessible.cpp @@ -0,0 +1,828 @@ +/* -*- Mode: C++; tab-width: 4; 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 "XULListboxAccessible.h" + +#include "Accessible-inl.h" +#include "nsAccessibilityService.h" +#include "nsAccUtils.h" +#include "DocAccessible.h" +#include "Role.h" +#include "States.h" + +#include "nsComponentManagerUtils.h" +#include "nsIAutoCompleteInput.h" +#include "nsIAutoCompletePopup.h" +#include "nsIDOMXULMenuListElement.h" +#include "nsIDOMXULMultSelectCntrlEl.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMXULPopupElement.h" +#include "nsIDOMXULSelectCntrlItemEl.h" +#include "nsIMutableArray.h" +#include "nsIPersistentProperties2.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULColumAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULColumAccessible:: + XULColumAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +role +XULColumAccessible::NativeRole() +{ + return roles::LIST; +} + +uint64_t +XULColumAccessible::NativeState() +{ + return states::READONLY; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULColumnItemAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULColumnItemAccessible:: + XULColumnItemAccessible(nsIContent* aContent, DocAccessible* aDoc) : + LeafAccessible(aContent, aDoc) +{ +} + +role +XULColumnItemAccessible::NativeRole() +{ + return roles::COLUMNHEADER; +} + +uint64_t +XULColumnItemAccessible::NativeState() +{ + return states::READONLY; +} + +uint8_t +XULColumnItemAccessible::ActionCount() +{ + return 1; +} + +void +XULColumnItemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click) + aName.AssignLiteral("click"); +} + +bool +XULColumnItemAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != eAction_Click) + return false; + + DoCommand(); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListboxAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULListboxAccessible:: + XULListboxAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULSelectControlAccessible(aContent, aDoc) +{ + nsIContent* parentContent = mContent->GetFlattenedTreeParent(); + if (parentContent) { + nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = + do_QueryInterface(parentContent); + if (autoCompletePopupElm) + mGenericTypes |= eAutoCompletePopup; + } + + if (IsMulticolumn()) + mGenericTypes |= eTable; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListboxAccessible: Accessible + +uint64_t +XULListboxAccessible::NativeState() +{ + // As a XULListboxAccessible we can have the following states: + // FOCUSED, READONLY, FOCUSABLE + + // Get focus status from base class + uint64_t states = Accessible::NativeState(); + + // see if we are multiple select if so set ourselves as such + + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::seltype, + nsGkAtoms::multiple, eCaseMatters)) { + states |= states::MULTISELECTABLE | states::EXTSELECTABLE; + } + + return states; +} + +/** + * Our value is the label of our ( first ) selected child. + */ +void +XULListboxAccessible::Value(nsString& aValue) +{ + aValue.Truncate(); + + nsCOMPtr<nsIDOMXULSelectControlElement> select(do_QueryInterface(mContent)); + if (select) { + nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem; + select->GetSelectedItem(getter_AddRefs(selectedItem)); + if (selectedItem) + selectedItem->GetLabel(aValue); + } +} + +role +XULListboxAccessible::NativeRole() +{ + // A richlistbox is used with the new autocomplete URL bar, and has a parent + // popup <panel>. + nsCOMPtr<nsIDOMXULPopupElement> xulPopup = + do_QueryInterface(mContent->GetParent()); + if (xulPopup) + return roles::COMBOBOX_LIST; + + return IsMulticolumn() ? roles::TABLE : roles::LISTBOX; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListboxAccessible: Table + +uint32_t +XULListboxAccessible::ColCount() +{ + nsIContent* headContent = nullptr; + for (nsIContent* childContent = mContent->GetFirstChild(); childContent; + childContent = childContent->GetNextSibling()) { + if (childContent->NodeInfo()->Equals(nsGkAtoms::listcols, + kNameSpaceID_XUL)) { + headContent = childContent; + } + } + if (!headContent) + return 0; + + uint32_t columnCount = 0; + for (nsIContent* childContent = headContent->GetFirstChild(); childContent; + childContent = childContent->GetNextSibling()) { + if (childContent->NodeInfo()->Equals(nsGkAtoms::listcol, + kNameSpaceID_XUL)) { + columnCount++; + } + } + + return columnCount; +} + +uint32_t +XULListboxAccessible::RowCount() +{ + nsCOMPtr<nsIDOMXULSelectControlElement> element(do_QueryInterface(mContent)); + + uint32_t itemCount = 0; + if(element) + element->GetItemCount(&itemCount); + + return itemCount; +} + +Accessible* +XULListboxAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) +{ + nsCOMPtr<nsIDOMXULSelectControlElement> control = + do_QueryInterface(mContent); + NS_ENSURE_TRUE(control, nullptr); + + nsCOMPtr<nsIDOMXULSelectControlItemElement> item; + control->GetItemAtIndex(aRowIndex, getter_AddRefs(item)); + if (!item) + return nullptr; + + nsCOMPtr<nsIContent> itemContent(do_QueryInterface(item)); + if (!itemContent) + return nullptr; + + Accessible* row = mDoc->GetAccessible(itemContent); + NS_ENSURE_TRUE(row, nullptr); + + return row->GetChildAt(aColumnIndex); +} + +bool +XULListboxAccessible::IsColSelected(uint32_t aColIdx) +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + int32_t selectedrowCount = 0; + nsresult rv = control->GetSelectedCount(&selectedrowCount); + NS_ENSURE_SUCCESS(rv, false); + + return selectedrowCount == static_cast<int32_t>(RowCount()); +} + +bool +XULListboxAccessible::IsRowSelected(uint32_t aRowIdx) +{ + nsCOMPtr<nsIDOMXULSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULSelectControlElement."); + + nsCOMPtr<nsIDOMXULSelectControlItemElement> item; + nsresult rv = control->GetItemAtIndex(aRowIdx, getter_AddRefs(item)); + NS_ENSURE_SUCCESS(rv, false); + + bool isSelected = false; + item->GetSelected(&isSelected); + return isSelected; +} + +bool +XULListboxAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) +{ + return IsRowSelected(aRowIdx); +} + +uint32_t +XULListboxAccessible::SelectedCellCount() +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + nsCOMPtr<nsIDOMNodeList> selectedItems; + control->GetSelectedItems(getter_AddRefs(selectedItems)); + if (!selectedItems) + return 0; + + uint32_t selectedItemsCount = 0; + nsresult rv = selectedItems->GetLength(&selectedItemsCount); + NS_ENSURE_SUCCESS(rv, 0); + + return selectedItemsCount * ColCount(); +} + +uint32_t +XULListboxAccessible::SelectedColCount() +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + int32_t selectedRowCount = 0; + nsresult rv = control->GetSelectedCount(&selectedRowCount); + NS_ENSURE_SUCCESS(rv, 0); + + return selectedRowCount > 0 && + selectedRowCount == static_cast<int32_t>(RowCount()) ? ColCount() : 0; +} + +uint32_t +XULListboxAccessible::SelectedRowCount() +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + int32_t selectedRowCount = 0; + nsresult rv = control->GetSelectedCount(&selectedRowCount); + NS_ENSURE_SUCCESS(rv, 0); + + return selectedRowCount >= 0 ? selectedRowCount : 0; +} + +void +XULListboxAccessible::SelectedCells(nsTArray<Accessible*>* aCells) +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + nsCOMPtr<nsIDOMNodeList> selectedItems; + control->GetSelectedItems(getter_AddRefs(selectedItems)); + if (!selectedItems) + return; + + uint32_t selectedItemsCount = 0; + DebugOnly<nsresult> rv = selectedItems->GetLength(&selectedItemsCount); + NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!"); + + for (uint32_t index = 0; index < selectedItemsCount; index++) { + nsCOMPtr<nsIDOMNode> itemNode; + selectedItems->Item(index, getter_AddRefs(itemNode)); + nsCOMPtr<nsIContent> itemContent(do_QueryInterface(itemNode)); + Accessible* item = mDoc->GetAccessible(itemContent); + + if (item) { + uint32_t cellCount = item->ChildCount(); + for (uint32_t cellIdx = 0; cellIdx < cellCount; cellIdx++) { + Accessible* cell = mChildren[cellIdx]; + if (cell->Role() == roles::CELL) + aCells->AppendElement(cell); + } + } + } +} + +void +XULListboxAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + nsCOMPtr<nsIDOMNodeList> selectedItems; + control->GetSelectedItems(getter_AddRefs(selectedItems)); + if (!selectedItems) + return; + + uint32_t selectedItemsCount = 0; + DebugOnly<nsresult> rv = selectedItems->GetLength(&selectedItemsCount); + NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!"); + + uint32_t colCount = ColCount(); + aCells->SetCapacity(selectedItemsCount * colCount); + aCells->AppendElements(selectedItemsCount * colCount); + + for (uint32_t selItemsIdx = 0, cellsIdx = 0; + selItemsIdx < selectedItemsCount; selItemsIdx++) { + + nsCOMPtr<nsIDOMNode> itemNode; + selectedItems->Item(selItemsIdx, getter_AddRefs(itemNode)); + nsCOMPtr<nsIDOMXULSelectControlItemElement> item = + do_QueryInterface(itemNode); + + if (item) { + int32_t itemIdx = -1; + control->GetIndexOfItem(item, &itemIdx); + if (itemIdx >= 0) + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++, cellsIdx++) + aCells->ElementAt(cellsIdx) = itemIdx * colCount + colIdx; + } + } +} + +void +XULListboxAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) +{ + uint32_t selColCount = SelectedColCount(); + aCols->SetCapacity(selColCount); + + for (uint32_t colIdx = 0; colIdx < selColCount; colIdx++) + aCols->AppendElement(colIdx); +} + +void +XULListboxAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + nsCOMPtr<nsIDOMNodeList> selectedItems; + control->GetSelectedItems(getter_AddRefs(selectedItems)); + if (!selectedItems) + return; + + uint32_t rowCount = 0; + DebugOnly<nsresult> rv = selectedItems->GetLength(&rowCount); + NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!"); + + if (!rowCount) + return; + + aRows->SetCapacity(rowCount); + aRows->AppendElements(rowCount); + + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { + nsCOMPtr<nsIDOMNode> itemNode; + selectedItems->Item(rowIdx, getter_AddRefs(itemNode)); + nsCOMPtr<nsIDOMXULSelectControlItemElement> item = + do_QueryInterface(itemNode); + + if (item) { + int32_t itemIdx = -1; + control->GetIndexOfItem(item, &itemIdx); + if (itemIdx >= 0) + aRows->ElementAt(rowIdx) = itemIdx; + } + } +} + +void +XULListboxAccessible::SelectRow(uint32_t aRowIdx) +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + nsCOMPtr<nsIDOMXULSelectControlItemElement> item; + control->GetItemAtIndex(aRowIdx, getter_AddRefs(item)); + control->SelectItem(item); +} + +void +XULListboxAccessible::UnselectRow(uint32_t aRowIdx) +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> control = + do_QueryInterface(mContent); + NS_ASSERTION(control, + "Doesn't implement nsIDOMXULMultiSelectControlElement."); + + nsCOMPtr<nsIDOMXULSelectControlItemElement> item; + control->GetItemAtIndex(aRowIdx, getter_AddRefs(item)); + control->RemoveItemFromSelection(item); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListboxAccessible: Widgets + +bool +XULListboxAccessible::IsWidget() const +{ + return true; +} + +bool +XULListboxAccessible::IsActiveWidget() const +{ + if (IsAutoCompletePopup()) { + nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = + do_QueryInterface(mContent->GetParent()); + + if (autoCompletePopupElm) { + bool isOpen = false; + autoCompletePopupElm->GetPopupOpen(&isOpen); + return isOpen; + } + } + return FocusMgr()->HasDOMFocus(mContent); +} + +bool +XULListboxAccessible::AreItemsOperable() const +{ + if (IsAutoCompletePopup()) { + nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = + do_QueryInterface(mContent->GetParent()); + + if (autoCompletePopupElm) { + bool isOpen = false; + autoCompletePopupElm->GetPopupOpen(&isOpen); + return isOpen; + } + } + return true; +} + +Accessible* +XULListboxAccessible::ContainerWidget() const +{ + if (IsAutoCompletePopup()) { + // This works for XUL autocompletes. It doesn't work for HTML forms + // autocomplete because of potential crossprocess calls (when autocomplete + // lives in content process while popup lives in chrome process). If that's + // a problem then rethink Widgets interface. + nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = + do_QueryInterface(mContent->GetParent()); + if (menuListElm) { + nsCOMPtr<nsIDOMNode> inputElm; + menuListElm->GetInputField(getter_AddRefs(inputElm)); + if (inputElm) { + nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm); + if (inputNode) { + Accessible* input = + mDoc->GetAccessible(inputNode); + return input ? input->ContainerWidget() : nullptr; + } + } + } + } + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListitemAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULListitemAccessible:: + XULListitemAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULMenuitemAccessible(aContent, aDoc) +{ + mIsCheckbox = mContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::type, + nsGkAtoms::checkbox, + eCaseMatters); + mType = eXULListItemType; + + // Walk XBL anonymous children for list items. Overrides the flag value from + // base XULMenuitemAccessible class. + mStateFlags &= ~eNoXBLKids; +} + +XULListitemAccessible::~XULListitemAccessible() +{ +} + +NS_IMPL_ISUPPORTS_INHERITED0(XULListitemAccessible, Accessible) + +Accessible* +XULListitemAccessible::GetListAccessible() const +{ + if (IsDefunct()) + return nullptr; + + nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem = + do_QueryInterface(mContent); + if (!listItem) + return nullptr; + + nsCOMPtr<nsIDOMXULSelectControlElement> list; + listItem->GetControl(getter_AddRefs(list)); + + nsCOMPtr<nsIContent> listContent(do_QueryInterface(list)); + if (!listContent) + return nullptr; + + return mDoc->GetAccessible(listContent); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListitemAccessible Accessible + +void +XULListitemAccessible::Description(nsString& aDesc) +{ + AccessibleWrap::Description(aDesc); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListitemAccessible: Accessible + +/** + * If there is a Listcell as a child ( not anonymous ) use it, otherwise + * default to getting the name from GetXULName + */ +ENameValueFlag +XULListitemAccessible::NativeName(nsString& aName) +{ + nsIContent* childContent = mContent->GetFirstChild(); + if (childContent) { + if (childContent->NodeInfo()->Equals(nsGkAtoms::listcell, + kNameSpaceID_XUL)) { + childContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName); + return eNameOK; + } + } + + return Accessible::NativeName(aName); +} + +role +XULListitemAccessible::NativeRole() +{ + Accessible* list = GetListAccessible(); + if (!list) { + NS_ERROR("No list accessible for listitem accessible!"); + return roles::NOTHING; + } + + if (list->Role() == roles::TABLE) + return roles::ROW; + + if (mIsCheckbox) + return roles::CHECK_RICH_OPTION; + + if (mParent && mParent->Role() == roles::COMBOBOX_LIST) + return roles::COMBOBOX_OPTION; + + return roles::RICH_OPTION; +} + +uint64_t +XULListitemAccessible::NativeState() +{ + if (mIsCheckbox) + return XULMenuitemAccessible::NativeState(); + + uint64_t states = NativeInteractiveState(); + + nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem = + do_QueryInterface(mContent); + + if (listItem) { + bool isSelected; + listItem->GetSelected(&isSelected); + if (isSelected) + states |= states::SELECTED; + + if (FocusMgr()->IsFocused(this)) + states |= states::FOCUSED; + } + + return states; +} + +uint64_t +XULListitemAccessible::NativeInteractiveState() const +{ + return NativelyUnavailable() || (mParent && mParent->NativelyUnavailable()) ? + states::UNAVAILABLE : states::FOCUSABLE | states::SELECTABLE; +} + +void +XULListitemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click && mIsCheckbox) { + uint64_t states = NativeState(); + if (states & states::CHECKED) + aName.AssignLiteral("uncheck"); + else + aName.AssignLiteral("check"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListitemAccessible: Widgets + +Accessible* +XULListitemAccessible::ContainerWidget() const +{ + return Parent(); +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULListCellAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULListCellAccessible:: + XULListCellAccessible(nsIContent* aContent, DocAccessible* aDoc) : + HyperTextAccessibleWrap(aContent, aDoc) +{ + mGenericTypes |= eTableCell; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_IMPL_ISUPPORTS_INHERITED0(XULListCellAccessible, + HyperTextAccessible) + +//////////////////////////////////////////////////////////////////////////////// +// XULListCellAccessible: TableCell + +TableAccessible* +XULListCellAccessible::Table() const +{ + Accessible* thisRow = Parent(); + if (!thisRow || thisRow->Role() != roles::ROW) + return nullptr; + + Accessible* table = thisRow->Parent(); + if (!table || table->Role() != roles::TABLE) + return nullptr; + + return table->AsTable(); +} + +uint32_t +XULListCellAccessible::ColIdx() const +{ + Accessible* row = Parent(); + if (!row) + return 0; + + int32_t indexInRow = IndexInParent(); + uint32_t colIdx = 0; + for (int32_t idx = 0; idx < indexInRow; idx++) { + Accessible* cell = row->GetChildAt(idx); + roles::Role role = cell->Role(); + if (role == roles::CELL || role == roles::GRID_CELL || + role == roles::ROWHEADER || role == roles::COLUMNHEADER) + colIdx++; + } + + return colIdx; +} + +uint32_t +XULListCellAccessible::RowIdx() const +{ + Accessible* row = Parent(); + if (!row) + return 0; + + Accessible* table = row->Parent(); + if (!table) + return 0; + + int32_t indexInTable = row->IndexInParent(); + uint32_t rowIdx = 0; + for (int32_t idx = 0; idx < indexInTable; idx++) { + row = table->GetChildAt(idx); + if (row->Role() == roles::ROW) + rowIdx++; + } + + return rowIdx; +} + +void +XULListCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells) +{ + TableAccessible* table = Table(); + NS_ASSERTION(table, "cell not in a table!"); + if (!table) + return; + + // Get column header cell from XUL listhead. + Accessible* list = nullptr; + + Accessible* tableAcc = table->AsAccessible(); + uint32_t tableChildCount = tableAcc->ChildCount(); + for (uint32_t childIdx = 0; childIdx < tableChildCount; childIdx++) { + Accessible* child = tableAcc->GetChildAt(childIdx); + if (child->Role() == roles::LIST) { + list = child; + break; + } + } + + if (list) { + Accessible* headerCell = list->GetChildAt(ColIdx()); + if (headerCell) { + aCells->AppendElement(headerCell); + return; + } + } + + // No column header cell from XUL markup, try to get it from ARIA markup. + TableCellAccessible::ColHeaderCells(aCells); +} + +bool +XULListCellAccessible::Selected() +{ + TableAccessible* table = Table(); + NS_ENSURE_TRUE(table, false); // we expect to be in a listbox (table) + + return table->IsRowSelected(RowIdx()); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListCellAccessible. Accessible implementation + +role +XULListCellAccessible::NativeRole() +{ + return roles::CELL; +} + +already_AddRefed<nsIPersistentProperties> +XULListCellAccessible::NativeAttributes() +{ + nsCOMPtr<nsIPersistentProperties> attributes = + HyperTextAccessibleWrap::NativeAttributes(); + + // "table-cell-index" attribute + TableAccessible* table = Table(); + if (!table) // we expect to be in a listbox (table) + return attributes.forget(); + + nsAutoString stringIdx; + stringIdx.AppendInt(table->CellIndexAt(RowIdx(), ColIdx())); + nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx); + + return attributes.forget(); +} diff --git a/accessible/xul/XULListboxAccessible.h b/accessible/xul/XULListboxAccessible.h new file mode 100644 index 000000000..057838035 --- /dev/null +++ b/accessible/xul/XULListboxAccessible.h @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 4; 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/. */ + +#ifndef mozilla_a11y_XULListboxAccessible_h__ +#define mozilla_a11y_XULListboxAccessible_h__ + +#include "BaseAccessibles.h" +#include "TableAccessible.h" +#include "TableCellAccessible.h" +#include "xpcAccessibleTable.h" +#include "xpcAccessibleTableCell.h" +#include "XULMenuAccessible.h" +#include "XULSelectControlAccessible.h" + +namespace mozilla { +namespace a11y { + +/** + * XULColumAccessible are accessible for list and tree columns elements + * (xul:treecols and xul:listcols). + */ +class XULColumAccessible : public AccessibleWrap +{ +public: + XULColumAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; +}; + +/** + * XULColumnItemAccessible are accessibles for list and tree column elements + * (xul:listcol and xul:treecol). + */ +class XULColumnItemAccessible : public LeafAccessible +{ +public: + XULColumnItemAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + + enum { eAction_Click = 0 }; +}; + +/* + * A class the represents the XUL Listbox widget. + */ +class XULListboxAccessible : public XULSelectControlAccessible, + public TableAccessible +{ +public: + XULListboxAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // TableAccessible + virtual uint32_t ColCount() override; + virtual uint32_t RowCount() override; + virtual Accessible* CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) override; + virtual bool IsColSelected(uint32_t aColIdx) override; + virtual bool IsRowSelected(uint32_t aRowIdx) override; + virtual bool IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) override; + virtual uint32_t SelectedCellCount() override; + virtual uint32_t SelectedColCount() override; + virtual uint32_t SelectedRowCount() override; + virtual void SelectedCells(nsTArray<Accessible*>* aCells) override; + virtual void SelectedCellIndices(nsTArray<uint32_t>* aCells) override; + virtual void SelectedColIndices(nsTArray<uint32_t>* aCols) override; + virtual void SelectedRowIndices(nsTArray<uint32_t>* aRows) override; + virtual void SelectRow(uint32_t aRowIdx) override; + virtual void UnselectRow(uint32_t aRowIdx) override; + virtual Accessible* AsAccessible() override { return this; } + + // Accessible + virtual void Value(nsString& aValue) override; + virtual TableAccessible* AsTable() override { return this; } + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // Widgets + virtual bool IsWidget() const override; + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; + + virtual Accessible* ContainerWidget() const override; + +protected: + virtual ~XULListboxAccessible() {} + + bool IsMulticolumn() { return ColCount() > 1; } +}; + +/** + * Listitems -- used in listboxes + */ +class XULListitemAccessible : public XULMenuitemAccessible +{ +public: + enum { eAction_Click = 0 }; + + NS_DECL_ISUPPORTS_INHERITED + + XULListitemAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual void Description(nsString& aDesc) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + virtual uint64_t NativeInteractiveState() const override; + + // Actions + virtual void ActionNameAt(uint8_t index, nsAString& aName) override; + + // Widgets + virtual Accessible* ContainerWidget() const override; + +protected: + virtual ~XULListitemAccessible(); + + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; + + // XULListitemAccessible + + /** + * Return listbox accessible for the listitem. + */ + Accessible* GetListAccessible() const; + +private: + bool mIsCheckbox; +}; + +/** + * Class represents xul:listcell. + */ +class XULListCellAccessible : public HyperTextAccessibleWrap, + public TableCellAccessible +{ +public: + XULListCellAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + // Accessible + virtual TableCellAccessible* AsTableCell() override { return this; } + virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override; + virtual a11y::role NativeRole() override; + + // TableCellAccessible + virtual TableAccessible* Table() const override; + virtual uint32_t ColIdx() const override; + virtual uint32_t RowIdx() const override; + virtual void ColHeaderCells(nsTArray<Accessible*>* aHeaderCells) override; + virtual bool Selected() override; + +protected: + virtual ~XULListCellAccessible() {} +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xul/XULMenuAccessible.cpp b/accessible/xul/XULMenuAccessible.cpp new file mode 100644 index 000000000..f93e9ad99 --- /dev/null +++ b/accessible/xul/XULMenuAccessible.cpp @@ -0,0 +1,591 @@ +/* -*- 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 "XULMenuAccessible.h" + +#include "Accessible-inl.h" +#include "nsAccessibilityService.h" +#include "nsAccUtils.h" +#include "DocAccessible.h" +#include "Role.h" +#include "States.h" +#include "XULFormControlAccessible.h" + +#include "nsIDOMElement.h" +#include "nsIDOMXULElement.h" +#include "nsIMutableArray.h" +#include "nsIDOMXULContainerElement.h" +#include "nsIDOMXULSelectCntrlItemEl.h" +#include "nsIDOMXULMultSelectCntrlEl.h" +#include "nsIDOMKeyEvent.h" +#include "nsIServiceManager.h" +#include "nsIPresShell.h" +#include "nsIContent.h" +#include "nsMenuBarFrame.h" +#include "nsMenuPopupFrame.h" + +#include "mozilla/Preferences.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/dom/Element.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULMenuitemAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULMenuitemAccessible:: + XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ + mStateFlags |= eNoXBLKids; +} + +uint64_t +XULMenuitemAccessible::NativeState() +{ + uint64_t state = Accessible::NativeState(); + + // Has Popup? + if (mContent->NodeInfo()->Equals(nsGkAtoms::menu, kNameSpaceID_XUL)) { + state |= states::HASPOPUP; + if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::open)) + state |= states::EXPANDED; + else + state |= states::COLLAPSED; + } + + // Checkable/checked? + static nsIContent::AttrValuesArray strings[] = + { &nsGkAtoms::radio, &nsGkAtoms::checkbox, nullptr }; + + if (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, strings, + eCaseMatters) >= 0) { + + // Checkable? + state |= states::CHECKABLE; + + // Checked? + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, + nsGkAtoms::_true, eCaseMatters)) + state |= states::CHECKED; + } + + // Combo box listitem + bool isComboboxOption = (Role() == roles::COMBOBOX_OPTION); + if (isComboboxOption) { + // Is selected? + bool isSelected = false; + nsCOMPtr<nsIDOMXULSelectControlItemElement> + item(do_QueryInterface(mContent)); + NS_ENSURE_TRUE(item, state); + item->GetSelected(&isSelected); + + // Is collapsed? + bool isCollapsed = false; + Accessible* parent = Parent(); + if (parent && parent->State() & states::INVISIBLE) + isCollapsed = true; + + if (isSelected) { + state |= states::SELECTED; + + // Selected and collapsed? + if (isCollapsed) { + // Set selected option offscreen/invisible according to combobox state + Accessible* grandParent = parent->Parent(); + if (!grandParent) + return state; + NS_ASSERTION(grandParent->Role() == roles::COMBOBOX, + "grandparent of combobox listitem is not combobox"); + uint64_t grandParentState = grandParent->State(); + state &= ~(states::OFFSCREEN | states::INVISIBLE); + state |= (grandParentState & states::OFFSCREEN) | + (grandParentState & states::INVISIBLE) | + (grandParentState & states::OPAQUE1); + } // isCollapsed + } // isSelected + } // ROLE_COMBOBOX_OPTION + + return state; +} + +uint64_t +XULMenuitemAccessible::NativeInteractiveState() const +{ + if (NativelyUnavailable()) { + // Note: keep in sinc with nsXULPopupManager::IsValidMenuItem() logic. + bool skipNavigatingDisabledMenuItem = true; + nsMenuFrame* menuFrame = do_QueryFrame(GetFrame()); + if (!menuFrame || !menuFrame->IsOnMenuBar()) { + skipNavigatingDisabledMenuItem = LookAndFeel:: + GetInt(LookAndFeel::eIntID_SkipNavigatingDisabledMenuItem, 0) != 0; + } + + if (skipNavigatingDisabledMenuItem) + return states::UNAVAILABLE; + + return states::UNAVAILABLE | states::FOCUSABLE | states::SELECTABLE; + } + + return states::FOCUSABLE | states::SELECTABLE; +} + +ENameValueFlag +XULMenuitemAccessible::NativeName(nsString& aName) +{ + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName); + return eNameOK; +} + +void +XULMenuitemAccessible::Description(nsString& aDescription) +{ + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::description, + aDescription); +} + +KeyBinding +XULMenuitemAccessible::AccessKey() const +{ + // Return menu accesskey: N or Alt+F. + static int32_t gMenuAccesskeyModifier = -1; // magic value of -1 indicates unitialized state + + // We do not use nsCoreUtils::GetAccesskeyFor() because accesskeys for + // menu are't registered by EventStateManager. + nsAutoString accesskey; + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, + accesskey); + if (accesskey.IsEmpty()) + return KeyBinding(); + + uint32_t modifierKey = 0; + + Accessible* parentAcc = Parent(); + if (parentAcc) { + if (parentAcc->NativeRole() == roles::MENUBAR) { + // If top level menu item, add Alt+ or whatever modifier text to string + // No need to cache pref service, this happens rarely + if (gMenuAccesskeyModifier == -1) { + // Need to initialize cached global accesskey pref + gMenuAccesskeyModifier = Preferences::GetInt("ui.key.menuAccessKey", 0); + } + + switch (gMenuAccesskeyModifier) { + case nsIDOMKeyEvent::DOM_VK_CONTROL: + modifierKey = KeyBinding::kControl; + break; + case nsIDOMKeyEvent::DOM_VK_ALT: + modifierKey = KeyBinding::kAlt; + break; + case nsIDOMKeyEvent::DOM_VK_META: + modifierKey = KeyBinding::kMeta; + break; + case nsIDOMKeyEvent::DOM_VK_WIN: + modifierKey = KeyBinding::kOS; + break; + } + } + } + + return KeyBinding(accesskey[0], modifierKey); +} + +KeyBinding +XULMenuitemAccessible::KeyboardShortcut() const +{ + nsAutoString keyElmId; + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyElmId); + if (keyElmId.IsEmpty()) + return KeyBinding(); + + nsIContent* keyElm = mContent->OwnerDoc()->GetElementById(keyElmId); + if (!keyElm) + return KeyBinding(); + + uint32_t key = 0; + + nsAutoString keyStr; + keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyStr); + if (keyStr.IsEmpty()) { + nsAutoString keyCodeStr; + keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyCodeStr); + nsresult errorCode; + key = keyStr.ToInteger(&errorCode, kAutoDetect); + } else { + key = keyStr[0]; + } + + nsAutoString modifiersStr; + keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr); + + uint32_t modifierMask = 0; + if (modifiersStr.Find("shift") != -1) + modifierMask |= KeyBinding::kShift; + if (modifiersStr.Find("alt") != -1) + modifierMask |= KeyBinding::kAlt; + if (modifiersStr.Find("meta") != -1) + modifierMask |= KeyBinding::kMeta; + if (modifiersStr.Find("os") != -1) + modifierMask |= KeyBinding::kOS; + if (modifiersStr.Find("control") != -1) + modifierMask |= KeyBinding::kControl; + if (modifiersStr.Find("accel") != -1) { + modifierMask |= KeyBinding::AccelModifier(); + } + + return KeyBinding(key, modifierMask); +} + +role +XULMenuitemAccessible::NativeRole() +{ + nsCOMPtr<nsIDOMXULContainerElement> xulContainer(do_QueryInterface(mContent)); + if (xulContainer) + return roles::PARENT_MENUITEM; + + if (mParent && mParent->Role() == roles::COMBOBOX_LIST) + return roles::COMBOBOX_OPTION; + + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + nsGkAtoms::radio, eCaseMatters)) + return roles::RADIO_MENU_ITEM; + + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + nsGkAtoms::checkbox, + eCaseMatters)) + return roles::CHECK_MENU_ITEM; + + return roles::MENUITEM; +} + +int32_t +XULMenuitemAccessible::GetLevelInternal() +{ + return nsAccUtils::GetLevelForXULContainerItem(mContent); +} + +bool +XULMenuitemAccessible::DoAction(uint8_t index) +{ + if (index == eAction_Click) { // default action + DoCommand(); + return true; + } + + return false; +} + +void +XULMenuitemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click) + aName.AssignLiteral("click"); +} + +uint8_t +XULMenuitemAccessible::ActionCount() +{ + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULMenuitemAccessible: Widgets + +bool +XULMenuitemAccessible::IsActiveWidget() const +{ + // Parent menu item is a widget, it's active when its popup is open. + nsIContent* menuPopupContent = mContent->GetFirstChild(); + if (menuPopupContent) { + nsMenuPopupFrame* menuPopupFrame = + do_QueryFrame(menuPopupContent->GetPrimaryFrame()); + return menuPopupFrame && menuPopupFrame->IsOpen(); + } + return false; +} + +bool +XULMenuitemAccessible::AreItemsOperable() const +{ + // Parent menu item is a widget, its items are operable when its popup is open. + nsIContent* menuPopupContent = mContent->GetFirstChild(); + if (menuPopupContent) { + nsMenuPopupFrame* menuPopupFrame = + do_QueryFrame(menuPopupContent->GetPrimaryFrame()); + return menuPopupFrame && menuPopupFrame->IsOpen(); + } + return false; +} + +Accessible* +XULMenuitemAccessible::ContainerWidget() const +{ + nsMenuFrame* menuFrame = do_QueryFrame(GetFrame()); + if (menuFrame) { + nsMenuParent* menuParent = menuFrame->GetMenuParent(); + if (menuParent) { + if (menuParent->IsMenuBar()) // menubar menu + return mParent; + + // a menupoup or parent menu item + if (menuParent->IsMenu()) + return mParent; + + // otherwise it's different kind of popups (like panel or tooltip), it + // shouldn't be a real case. + } + } + return nullptr; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULMenuSeparatorAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULMenuSeparatorAccessible:: + XULMenuSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULMenuitemAccessible(aContent, aDoc) +{ +} + +uint64_t +XULMenuSeparatorAccessible::NativeState() +{ + // Isn't focusable, but can be offscreen/invisible -- only copy those states + return XULMenuitemAccessible::NativeState() & + (states::OFFSCREEN | states::INVISIBLE); +} + +ENameValueFlag +XULMenuSeparatorAccessible::NativeName(nsString& aName) +{ + return eNameOK; +} + +role +XULMenuSeparatorAccessible::NativeRole() +{ + return roles::SEPARATOR; +} + +bool +XULMenuSeparatorAccessible::DoAction(uint8_t index) +{ + return false; +} + +void +XULMenuSeparatorAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + aName.Truncate(); +} + +uint8_t +XULMenuSeparatorAccessible::ActionCount() +{ + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULMenupopupAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULMenupopupAccessible:: + XULMenupopupAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULSelectControlAccessible(aContent, aDoc) +{ + nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame()); + if (menuPopupFrame && menuPopupFrame->IsMenu()) + mType = eMenuPopupType; + + // May be the anonymous <menupopup> inside <menulist> (a combobox) + mSelectControl = do_QueryInterface(mContent->GetFlattenedTreeParent()); + if (!mSelectControl) + mGenericTypes &= ~eSelect; + + mStateFlags |= eNoXBLKids; +} + +uint64_t +XULMenupopupAccessible::NativeState() +{ + uint64_t state = Accessible::NativeState(); + +#ifdef DEBUG + // We are onscreen if our parent is active + bool isActive = mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::menuactive); + if (!isActive) { + Accessible* parent = Parent(); + if (parent) { + nsIContent* parentContent = parent->GetContent(); + if (parentContent) + isActive = parentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::open); + } + } + + NS_ASSERTION(isActive || (state & states::INVISIBLE), + "XULMenupopup doesn't have INVISIBLE when it's inactive"); +#endif + + if (state & states::INVISIBLE) + state |= states::OFFSCREEN | states::COLLAPSED; + + return state; +} + +ENameValueFlag +XULMenupopupAccessible::NativeName(nsString& aName) +{ + nsIContent* content = mContent; + while (content && aName.IsEmpty()) { + content->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName); + content = content->GetFlattenedTreeParent(); + } + + return eNameOK; +} + +role +XULMenupopupAccessible::NativeRole() +{ + // If accessible is not bound to the tree (this happens while children are + // cached) return general role. + if (mParent) { + roles::Role role = mParent->Role(); + if (role == roles::COMBOBOX || role == roles::AUTOCOMPLETE) + return roles::COMBOBOX_LIST; + + if (role == roles::PUSHBUTTON) { + // Some widgets like the search bar have several popups, owned by buttons. + Accessible* grandParent = mParent->Parent(); + if (grandParent && grandParent->Role() == roles::AUTOCOMPLETE) + return roles::COMBOBOX_LIST; + } + } + + return roles::MENUPOPUP; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULMenupopupAccessible: Widgets + +bool +XULMenupopupAccessible::IsWidget() const +{ + return true; +} + +bool +XULMenupopupAccessible::IsActiveWidget() const +{ + // If menupopup is a widget (the case of context menus) then active when open. + nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame()); + return menuPopupFrame && menuPopupFrame->IsOpen(); +} + +bool +XULMenupopupAccessible::AreItemsOperable() const +{ + nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame()); + return menuPopupFrame && menuPopupFrame->IsOpen(); +} + +Accessible* +XULMenupopupAccessible::ContainerWidget() const +{ + DocAccessible* document = Document(); + + nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame()); + while (menuPopupFrame) { + Accessible* menuPopup = + document->GetAccessible(menuPopupFrame->GetContent()); + if (!menuPopup) // shouldn't be a real case + return nullptr; + + nsMenuFrame* menuFrame = do_QueryFrame(menuPopupFrame->GetParent()); + if (!menuFrame) // context menu or popups + return nullptr; + + nsMenuParent* menuParent = menuFrame->GetMenuParent(); + if (!menuParent) // menulist or menubutton + return menuPopup->Parent(); + + if (menuParent->IsMenuBar()) { // menubar menu + nsMenuBarFrame* menuBarFrame = static_cast<nsMenuBarFrame*>(menuParent); + return document->GetAccessible(menuBarFrame->GetContent()); + } + + // different kind of popups like panel or tooltip + if (!menuParent->IsMenu()) + return nullptr; + + menuPopupFrame = static_cast<nsMenuPopupFrame*>(menuParent); + } + + NS_NOTREACHED("Shouldn't be a real case."); + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULMenubarAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULMenubarAccessible:: + XULMenubarAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +ENameValueFlag +XULMenubarAccessible::NativeName(nsString& aName) +{ + aName.AssignLiteral("Application"); + return eNameOK; +} + +role +XULMenubarAccessible::NativeRole() +{ + return roles::MENUBAR; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULMenubarAccessible: Widgets + +bool +XULMenubarAccessible::IsActiveWidget() const +{ + nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame()); + return menuBarFrame && menuBarFrame->IsActive(); +} + +bool +XULMenubarAccessible::AreItemsOperable() const +{ + return true; +} + +Accessible* +XULMenubarAccessible::CurrentItem() +{ + nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame()); + if (menuBarFrame) { + nsMenuFrame* menuFrame = menuBarFrame->GetCurrentMenuItem(); + if (menuFrame) { + nsIContent* menuItemNode = menuFrame->GetContent(); + return mDoc->GetAccessible(menuItemNode); + } + } + return nullptr; +} + +void +XULMenubarAccessible::SetCurrentItem(Accessible* aItem) +{ + NS_ERROR("XULMenubarAccessible::SetCurrentItem not implemented"); +} diff --git a/accessible/xul/XULMenuAccessible.h b/accessible/xul/XULMenuAccessible.h new file mode 100644 index 000000000..6280db17d --- /dev/null +++ b/accessible/xul/XULMenuAccessible.h @@ -0,0 +1,122 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_XULMenuAccessible_h__ +#define mozilla_a11y_XULMenuAccessible_h__ + +#include "AccessibleWrap.h" +#include "nsIDOMXULSelectCntrlEl.h" +#include "XULSelectControlAccessible.h" + +namespace mozilla { +namespace a11y { + +/** + * Used for XUL menu, menuitem elements. + */ +class XULMenuitemAccessible : public AccessibleWrap +{ +public: + enum { eAction_Click = 0 }; + + XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual void Description(nsString& aDescription) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + virtual uint64_t NativeInteractiveState() const override; + virtual int32_t GetLevelInternal() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + virtual KeyBinding AccessKey() const override; + virtual KeyBinding KeyboardShortcut() const override; + + // Widgets + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; + virtual Accessible* ContainerWidget() const override; + +protected: + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; +}; + +/** + * Used for XUL menuseparator element. + */ +class XULMenuSeparatorAccessible : public XULMenuitemAccessible +{ +public: + XULMenuSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + +protected: + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; +}; + + +/** + * Used for XUL menupopup and panel. + */ +class XULMenupopupAccessible : public XULSelectControlAccessible +{ +public: + XULMenupopupAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + + // Widgets + virtual bool IsWidget() const override; + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; + + virtual Accessible* ContainerWidget() const override; + +protected: + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; +}; + +/** + * Used for XUL menubar element. + */ +class XULMenubarAccessible : public AccessibleWrap +{ +public: + XULMenubarAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + + // Widget + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; + virtual Accessible* CurrentItem() override; + virtual void SetCurrentItem(Accessible* aItem) override; + +protected: + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xul/XULSelectControlAccessible.cpp b/accessible/xul/XULSelectControlAccessible.cpp new file mode 100644 index 000000000..a1cc65d78 --- /dev/null +++ b/accessible/xul/XULSelectControlAccessible.cpp @@ -0,0 +1,256 @@ +/* -*- 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 "XULSelectControlAccessible.h" + +#include "nsAccessibilityService.h" +#include "DocAccessible.h" + +#include "nsIDOMXULContainerElement.h" +#include "nsIDOMXULSelectCntrlItemEl.h" +#include "nsIDOMXULMultSelectCntrlEl.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMElement.h" +#include "nsIDOMXULElement.h" +#include "nsIMutableArray.h" +#include "nsIServiceManager.h" + +#include "mozilla/dom/Element.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULSelectControlAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULSelectControlAccessible:: + XULSelectControlAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ + mGenericTypes |= eSelect; + mSelectControl = do_QueryInterface(aContent); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULSelectControlAccessible: Accessible + +void +XULSelectControlAccessible::Shutdown() +{ + mSelectControl = nullptr; + AccessibleWrap::Shutdown(); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULSelectControlAccessible: SelectAccessible + +void +XULSelectControlAccessible::SelectedItems(nsTArray<Accessible*>* aItems) +{ + // For XUL multi-select control + nsCOMPtr<nsIDOMXULMultiSelectControlElement> xulMultiSelect = + do_QueryInterface(mSelectControl); + if (xulMultiSelect) { + int32_t length = 0; + xulMultiSelect->GetSelectedCount(&length); + for (int32_t index = 0; index < length; index++) { + nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm; + xulMultiSelect->MultiGetSelectedItem(index, getter_AddRefs(itemElm)); + nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm)); + Accessible* item = mDoc->GetAccessible(itemNode); + if (item) + aItems->AppendElement(item); + } + } else { // Single select? + nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm; + mSelectControl->GetSelectedItem(getter_AddRefs(itemElm)); + nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm)); + if (itemNode) { + Accessible* item = mDoc->GetAccessible(itemNode); + if (item) + aItems->AppendElement(item); + } + } +} + +Accessible* +XULSelectControlAccessible::GetSelectedItem(uint32_t aIndex) +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = + do_QueryInterface(mSelectControl); + + nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm; + if (multiSelectControl) + multiSelectControl->MultiGetSelectedItem(aIndex, getter_AddRefs(itemElm)); + else if (aIndex == 0) + mSelectControl->GetSelectedItem(getter_AddRefs(itemElm)); + + nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm)); + return itemNode && mDoc ? mDoc->GetAccessible(itemNode) : nullptr; +} + +uint32_t +XULSelectControlAccessible::SelectedItemCount() +{ + // For XUL multi-select control + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = + do_QueryInterface(mSelectControl); + if (multiSelectControl) { + int32_t count = 0; + multiSelectControl->GetSelectedCount(&count); + return count; + } + + // For XUL single-select control/menulist + int32_t index; + mSelectControl->GetSelectedIndex(&index); + return (index >= 0) ? 1 : 0; +} + +bool +XULSelectControlAccessible::AddItemToSelection(uint32_t aIndex) +{ + Accessible* item = GetChildAt(aIndex); + if (!item) + return false; + + nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm = + do_QueryInterface(item->GetContent()); + if (!itemElm) + return false; + + bool isItemSelected = false; + itemElm->GetSelected(&isItemSelected); + if (isItemSelected) + return true; + + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = + do_QueryInterface(mSelectControl); + + if (multiSelectControl) + multiSelectControl->AddItemToSelection(itemElm); + else + mSelectControl->SetSelectedItem(itemElm); + + return true; +} + +bool +XULSelectControlAccessible::RemoveItemFromSelection(uint32_t aIndex) +{ + Accessible* item = GetChildAt(aIndex); + if (!item) + return false; + + nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm = + do_QueryInterface(item->GetContent()); + if (!itemElm) + return false; + + bool isItemSelected = false; + itemElm->GetSelected(&isItemSelected); + if (!isItemSelected) + return true; + + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = + do_QueryInterface(mSelectControl); + + if (multiSelectControl) + multiSelectControl->RemoveItemFromSelection(itemElm); + else + mSelectControl->SetSelectedItem(nullptr); + + return true; +} + +bool +XULSelectControlAccessible::IsItemSelected(uint32_t aIndex) +{ + Accessible* item = GetChildAt(aIndex); + if (!item) + return false; + + nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm = + do_QueryInterface(item->GetContent()); + if (!itemElm) + return false; + + bool isItemSelected = false; + itemElm->GetSelected(&isItemSelected); + return isItemSelected; +} + +bool +XULSelectControlAccessible::UnselectAll() +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = + do_QueryInterface(mSelectControl); + multiSelectControl ? + multiSelectControl->ClearSelection() : mSelectControl->SetSelectedIndex(-1); + + return true; +} + +bool +XULSelectControlAccessible::SelectAll() +{ + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = + do_QueryInterface(mSelectControl); + if (multiSelectControl) { + multiSelectControl->SelectAll(); + return true; + } + + // otherwise, don't support this method + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULSelectControlAccessible: Widgets + +Accessible* +XULSelectControlAccessible::CurrentItem() +{ + if (!mSelectControl) + return nullptr; + + nsCOMPtr<nsIDOMXULSelectControlItemElement> currentItemElm; + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = + do_QueryInterface(mSelectControl); + if (multiSelectControl) + multiSelectControl->GetCurrentItem(getter_AddRefs(currentItemElm)); + else + mSelectControl->GetSelectedItem(getter_AddRefs(currentItemElm)); + + nsCOMPtr<nsINode> DOMNode; + if (currentItemElm) + DOMNode = do_QueryInterface(currentItemElm); + + if (DOMNode) { + DocAccessible* document = Document(); + if (document) + return document->GetAccessible(DOMNode); + } + + return nullptr; +} + +void +XULSelectControlAccessible::SetCurrentItem(Accessible* aItem) +{ + if (!mSelectControl) + return; + + nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm = + do_QueryInterface(aItem->GetContent()); + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = + do_QueryInterface(mSelectControl); + if (multiSelectControl) + multiSelectControl->SetCurrentItem(itemElm); + else + mSelectControl->SetSelectedItem(itemElm); +} diff --git a/accessible/xul/XULSelectControlAccessible.h b/accessible/xul/XULSelectControlAccessible.h new file mode 100644 index 000000000..c4886e26e --- /dev/null +++ b/accessible/xul/XULSelectControlAccessible.h @@ -0,0 +1,52 @@ +/* -*- 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_XULSelectControlAccessible_h__ +#define mozilla_a11y_XULSelectControlAccessible_h__ + +#include "AccessibleWrap.h" +#include "nsIDOMXULSelectCntrlEl.h" + +namespace mozilla { +namespace a11y { + +/** + * The basic implementation of accessible selection for XUL select controls. + */ +class XULSelectControlAccessible : public AccessibleWrap +{ +public: + XULSelectControlAccessible(nsIContent* aContent, DocAccessible* aDoc); + virtual ~XULSelectControlAccessible() {} + + // Accessible + virtual void Shutdown() override; + + // SelectAccessible + virtual void SelectedItems(nsTArray<Accessible*>* aItems) override; + virtual uint32_t SelectedItemCount() override; + virtual Accessible* GetSelectedItem(uint32_t aIndex) override; + virtual bool IsItemSelected(uint32_t aIndex) override; + virtual bool AddItemToSelection(uint32_t aIndex) override; + virtual bool RemoveItemFromSelection(uint32_t aIndex) override; + virtual bool SelectAll() override; + virtual bool UnselectAll() override; + + // Widgets + virtual Accessible* CurrentItem() override; + virtual void SetCurrentItem(Accessible* aItem) override; + +protected: + // nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have + // one of these if the widget is valid and not defunct + nsCOMPtr<nsIDOMXULSelectControlElement> mSelectControl; +}; + +} // namespace a11y +} // namespace mozilla + +#endif + diff --git a/accessible/xul/XULSliderAccessible.cpp b/accessible/xul/XULSliderAccessible.cpp new file mode 100644 index 000000000..476cb17eb --- /dev/null +++ b/accessible/xul/XULSliderAccessible.cpp @@ -0,0 +1,214 @@ +/* -*- 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 "XULSliderAccessible.h" + +#include "nsAccessibilityService.h" +#include "Role.h" +#include "States.h" + +#include "nsIFrame.h" +#include "mozilla/dom/Element.h" +#include "mozilla/FloatingPoint.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULSliderAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULSliderAccessible:: + XULSliderAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ + mStateFlags |= eHasNumericValue | eNoXBLKids; +} + +// Accessible + +role +XULSliderAccessible::NativeRole() +{ + return roles::SLIDER; +} + +uint64_t +XULSliderAccessible::NativeInteractiveState() const + { + if (NativelyUnavailable()) + return states::UNAVAILABLE; + + nsIContent* sliderElm = GetSliderElement(); + if (sliderElm) { + nsIFrame* frame = sliderElm->GetPrimaryFrame(); + if (frame && frame->IsFocusable()) + return states::FOCUSABLE; + } + + return 0; +} + +bool +XULSliderAccessible::NativelyUnavailable() const +{ + return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, + nsGkAtoms::_true, eCaseMatters); +} + +void +XULSliderAccessible::Value(nsString& aValue) +{ + GetSliderAttr(nsGkAtoms::curpos, aValue); +} + +uint8_t +XULSliderAccessible::ActionCount() +{ + return 1; +} + +void +XULSliderAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + aName.Truncate(); + if (aIndex == 0) + aName.AssignLiteral("activate"); +} + +bool +XULSliderAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != 0) + return false; + + nsIContent* sliderElm = GetSliderElement(); + if (sliderElm) + DoCommand(sliderElm); + + return true; +} + +double +XULSliderAccessible::MaxValue() const +{ + double value = AccessibleWrap::MaxValue(); + return IsNaN(value) ? GetSliderAttr(nsGkAtoms::maxpos) : value; +} + +double +XULSliderAccessible::MinValue() const +{ + double value = AccessibleWrap::MinValue(); + return IsNaN(value) ? GetSliderAttr(nsGkAtoms::minpos) : value; +} + +double +XULSliderAccessible::Step() const +{ + double value = AccessibleWrap::Step(); + return IsNaN(value) ? GetSliderAttr(nsGkAtoms::increment) : value; +} + +double +XULSliderAccessible::CurValue() const +{ + double value = AccessibleWrap::CurValue(); + return IsNaN(value) ? GetSliderAttr(nsGkAtoms::curpos) : value; +} + +bool +XULSliderAccessible::SetCurValue(double aValue) +{ + if (AccessibleWrap::SetCurValue(aValue)) + return true; + + return SetSliderAttr(nsGkAtoms::curpos, aValue); +} + +// Utils + +nsIContent* +XULSliderAccessible::GetSliderElement() const +{ + if (!mSliderNode) { + // XXX: we depend on anonymous content. + mSliderNode = mContent->OwnerDoc()-> + GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, + NS_LITERAL_STRING("slider")); + } + + return mSliderNode; +} + +nsresult +XULSliderAccessible::GetSliderAttr(nsIAtom* aName, nsAString& aValue) const +{ + aValue.Truncate(); + + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsIContent* sliderElm = GetSliderElement(); + if (sliderElm) + sliderElm->GetAttr(kNameSpaceID_None, aName, aValue); + + return NS_OK; +} + +nsresult +XULSliderAccessible::SetSliderAttr(nsIAtom* aName, const nsAString& aValue) +{ + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsIContent* sliderElm = GetSliderElement(); + if (sliderElm) + sliderElm->SetAttr(kNameSpaceID_None, aName, aValue, true); + + return NS_OK; +} + +double +XULSliderAccessible::GetSliderAttr(nsIAtom* aName) const +{ + nsAutoString attrValue; + nsresult rv = GetSliderAttr(aName, attrValue); + if (NS_FAILED(rv)) + return UnspecifiedNaN<double>(); + + nsresult error = NS_OK; + double value = attrValue.ToDouble(&error); + return NS_FAILED(error) ? UnspecifiedNaN<double>() : value; +} + +bool +XULSliderAccessible::SetSliderAttr(nsIAtom* aName, double aValue) +{ + nsAutoString value; + value.AppendFloat(aValue); + + return NS_SUCCEEDED(SetSliderAttr(aName, value)); +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULThumbAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULThumbAccessible:: + XULThumbAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULThumbAccessible: Accessible + +role +XULThumbAccessible::NativeRole() +{ + return roles::INDICATOR; +} + diff --git a/accessible/xul/XULSliderAccessible.h b/accessible/xul/XULSliderAccessible.h new file mode 100644 index 000000000..72c914c26 --- /dev/null +++ b/accessible/xul/XULSliderAccessible.h @@ -0,0 +1,75 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_XULSliderAccessible_h__ +#define mozilla_a11y_XULSliderAccessible_h__ + +#include "AccessibleWrap.h" + +#include "nsIDOMElement.h" + +namespace mozilla { +namespace a11y { + +/** + * Used for XUL slider and scale elements. + */ +class XULSliderAccessible : public AccessibleWrap +{ +public: + XULSliderAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual void Value(nsString& aValue) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeInteractiveState() const override; + virtual bool NativelyUnavailable() const override; + + // Value + virtual double MaxValue() const override; + virtual double MinValue() const override; + virtual double CurValue() const override; + virtual double Step() const override; + virtual bool SetCurValue(double aValue) override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + +protected: + /** + * Return anonymous slider element. + */ + nsIContent* GetSliderElement() const; + + nsresult GetSliderAttr(nsIAtom *aName, nsAString& aValue) const; + nsresult SetSliderAttr(nsIAtom *aName, const nsAString& aValue); + + double GetSliderAttr(nsIAtom *aName) const; + bool SetSliderAttr(nsIAtom *aName, double aValue); + +private: + mutable nsCOMPtr<nsIContent> mSliderNode; +}; + + +/** + * Used for slider's thumb element. + */ +class XULThumbAccessible : public AccessibleWrap +{ +public: + XULThumbAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif + diff --git a/accessible/xul/XULTabAccessible.cpp b/accessible/xul/XULTabAccessible.cpp new file mode 100644 index 000000000..e525cf36d --- /dev/null +++ b/accessible/xul/XULTabAccessible.cpp @@ -0,0 +1,209 @@ +/* -*- 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 "nsAccUtils.h" +#include "Relation.h" +#include "Role.h" +#include "States.h" + +// NOTE: alphabetically ordered +#include "nsIDocument.h" +#include "nsIDOMXULSelectCntrlEl.h" +#include "nsIDOMXULSelectCntrlItemEl.h" +#include "nsIDOMXULRelatedElement.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULTabAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTabAccessible:: + XULTabAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTabAccessible: Accessible + +uint8_t +XULTabAccessible::ActionCount() +{ + return 1; +} + +void +XULTabAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Switch) + aName.AssignLiteral("switch"); +} + +bool +XULTabAccessible::DoAction(uint8_t index) +{ + if (index == eAction_Switch) { + nsCOMPtr<nsIDOMXULElement> tab(do_QueryInterface(mContent)); + if (tab) { + tab->Click(); + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTabAccessible: Accessible + +role +XULTabAccessible::NativeRole() +{ + return roles::PAGETAB; +} + +uint64_t +XULTabAccessible::NativeState() +{ + // 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<nsIDOMXULSelectControlItemElement> tab(do_QueryInterface(mContent)); + if (tab) { + bool selected = false; + if (NS_SUCCEEDED(tab->GetSelected(&selected)) && selected) + state |= states::SELECTED; + + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::pinned, + nsGkAtoms::_true, eCaseMatters)) + state |= states::PINNED; + + } + + return state; +} + +uint64_t +XULTabAccessible::NativeInteractiveState() const +{ + uint64_t state = Accessible::NativeInteractiveState(); + return (state & states::UNAVAILABLE) ? state : state | states::SELECTABLE; +} + +Relation +XULTabAccessible::RelationByType(RelationType aType) +{ + Relation rel = AccessibleWrap::RelationByType(aType); + if (aType != RelationType::LABEL_FOR) + return rel; + + // Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible. + nsCOMPtr<nsIDOMXULRelatedElement> tabsElm = + do_QueryInterface(mContent->GetParent()); + if (!tabsElm) + return rel; + + nsCOMPtr<nsIDOMNode> domNode(DOMNode()); + nsCOMPtr<nsIDOMNode> tabpanelNode; + tabsElm->GetRelatedElement(domNode, getter_AddRefs(tabpanelNode)); + if (!tabpanelNode) + return rel; + + nsCOMPtr<nsIContent> tabpanelContent(do_QueryInterface(tabpanelNode)); + rel.AppendTarget(mDoc, tabpanelContent); + return rel; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULTabsAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTabsAccessible:: + XULTabsAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULSelectControlAccessible(aContent, aDoc) +{ +} + +role +XULTabsAccessible::NativeRole() +{ + return roles::PAGETABLIST; +} + +uint8_t +XULTabsAccessible::ActionCount() +{ + return 0; +} + +void +XULTabsAccessible::Value(nsString& aValue) +{ + aValue.Truncate(); +} + +ENameValueFlag +XULTabsAccessible::NativeName(nsString& aName) +{ + // no name + return eNameOK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULTabpanelsAccessible +//////////////////////////////////////////////////////////////////////////////// + +role +XULTabpanelsAccessible::NativeRole() +{ + return roles::PANE; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTabpanelAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTabpanelAccessible:: + XULTabpanelAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +role +XULTabpanelAccessible::NativeRole() +{ + return roles::PROPERTYPAGE; +} + +Relation +XULTabpanelAccessible::RelationByType(RelationType aType) +{ + Relation rel = AccessibleWrap::RelationByType(aType); + if (aType != RelationType::LABELLED_BY) + return rel; + + // Expose 'LABELLED_BY' relation on tabpanel accessible for tab accessible. + nsCOMPtr<nsIDOMXULRelatedElement> tabpanelsElm = + do_QueryInterface(mContent->GetParent()); + if (!tabpanelsElm) + return rel; + + nsCOMPtr<nsIDOMNode> domNode(DOMNode()); + nsCOMPtr<nsIDOMNode> tabNode; + tabpanelsElm->GetRelatedElement(domNode, getter_AddRefs(tabNode)); + if (!tabNode) + return rel; + + nsCOMPtr<nsIContent> tabContent(do_QueryInterface(tabNode)); + rel.AppendTarget(mDoc, tabContent); + return rel; +} diff --git a/accessible/xul/XULTabAccessible.h b/accessible/xul/XULTabAccessible.h new file mode 100644 index 000000000..0c10c0679 --- /dev/null +++ b/accessible/xul/XULTabAccessible.h @@ -0,0 +1,96 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_XULTabAccessible_h__ +#define mozilla_a11y_XULTabAccessible_h__ + +// NOTE: alphabetically ordered +#include "XULMenuAccessible.h" +#include "XULSelectControlAccessible.h" + +namespace mozilla { +namespace a11y { + +/** + * An individual tab, xul:tab element. + */ +class XULTabAccessible : public AccessibleWrap +{ +public: + enum { eAction_Switch = 0 }; + + XULTabAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + virtual uint64_t NativeInteractiveState() const override; + virtual Relation RelationByType(RelationType aType) override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; +}; + + +/** + * A container of tab objects, xul:tabs element. + */ +class XULTabsAccessible : public XULSelectControlAccessible +{ +public: + XULTabsAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual void Value(nsString& aValue) override; + virtual a11y::role NativeRole() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + +protected: + // Accessible + virtual ENameValueFlag NativeName(nsString& aName) override; +}; + + +/** + * A container of tab panels, xul:tabpanels element. + */ +class XULTabpanelsAccessible : public AccessibleWrap +{ +public: + XULTabpanelsAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) + { mType = eXULTabpanelsType; } + + // Accessible + virtual a11y::role NativeRole() override; +}; + + +/** + * A tabpanel object, child elements of xul:tabpanels element. + * + * XXX: we need to move the class logic into generic class since + * for example we do not create instance of this class for XUL textbox used as + * a tabpanel. + */ +class XULTabpanelAccessible : public AccessibleWrap +{ +public: + XULTabpanelAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Accessible + virtual a11y::role NativeRole() override; + virtual Relation RelationByType(RelationType aType) override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif + diff --git a/accessible/xul/XULTreeAccessible.cpp b/accessible/xul/XULTreeAccessible.cpp new file mode 100644 index 000000000..88153dc2d --- /dev/null +++ b/accessible/xul/XULTreeAccessible.cpp @@ -0,0 +1,1184 @@ +/* -*- 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 "XULTreeAccessible.h" + +#include "Accessible-inl.h" +#include "DocAccessible-inl.h" +#include "nsAccCache.h" +#include "nsAccUtils.h" +#include "nsCoreUtils.h" +#include "nsEventShell.h" +#include "DocAccessible.h" +#include "Relation.h" +#include "Role.h" +#include "States.h" +#include "XULTreeGridAccessible.h" +#include "nsQueryObject.h" + +#include "nsComponentManagerUtils.h" +#include "nsIAccessibleRelation.h" +#include "nsIAutoCompleteInput.h" +#include "nsIAutoCompletePopup.h" +#include "nsIBoxObject.h" +#include "nsIDOMXULElement.h" +#include "nsIDOMXULMenuListElement.h" +#include "nsIDOMXULMultSelectCntrlEl.h" +#include "nsIDOMXULTreeElement.h" +#include "nsITreeSelection.h" +#include "nsIMutableArray.h" +#include "nsTreeBodyFrame.h" +#include "nsTreeColumns.h" +#include "nsTreeUtils.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTreeAccessible:: + XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc, + nsTreeBodyFrame* aTreeFrame) : + AccessibleWrap(aContent, aDoc), + mAccessibleCache(kDefaultTreeCacheLength) +{ + mType = eXULTreeType; + mGenericTypes |= eSelect; + + nsCOMPtr<nsITreeView> view = aTreeFrame->GetExistingView(); + mTreeView = view; + + mTree = nsCoreUtils::GetTreeBoxObject(aContent); + NS_ASSERTION(mTree, "Can't get mTree!\n"); + + nsIContent* parentContent = mContent->GetParent(); + if (parentContent) { + nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = + do_QueryInterface(parentContent); + if (autoCompletePopupElm) + mGenericTypes |= eAutoCompletePopup; + } +} + +XULTreeAccessible::~XULTreeAccessible() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: nsISupports and cycle collection implementation + +NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeAccessible, Accessible, + mTree, mAccessibleCache) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeAccessible) +NS_INTERFACE_MAP_END_INHERITING(Accessible) + +NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, Accessible) +NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, Accessible) + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: Accessible implementation + +uint64_t +XULTreeAccessible::NativeState() +{ + // Get focus status from base class. + uint64_t state = Accessible::NativeState(); + + // readonly state + state |= states::READONLY; + + // multiselectable state. + if (!mTreeView) + return state; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + NS_ENSURE_TRUE(selection, state); + + bool isSingle = false; + nsresult rv = selection->GetSingle(&isSingle); + NS_ENSURE_SUCCESS(rv, state); + + if (!isSingle) + state |= states::MULTISELECTABLE; + + return state; +} + +void +XULTreeAccessible::Value(nsString& aValue) +{ + aValue.Truncate(); + if (!mTreeView) + return; + + // Return the value is the first selected child. + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (!selection) + return; + + int32_t currentIndex; + selection->GetCurrentIndex(¤tIndex); + if (currentIndex >= 0) { + nsCOMPtr<nsITreeColumn> keyCol; + + nsCOMPtr<nsITreeColumns> cols; + mTree->GetColumns(getter_AddRefs(cols)); + if (cols) + cols->GetKeyColumn(getter_AddRefs(keyCol)); + + mTreeView->GetCellText(currentIndex, keyCol, aValue); + } + +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: Accessible implementation + +void +XULTreeAccessible::Shutdown() +{ + if (mDoc && !mDoc->IsDefunct()) { + UnbindCacheEntriesFromDocument(mAccessibleCache); + } + + mTree = nullptr; + mTreeView = nullptr; + + AccessibleWrap::Shutdown(); +} + +role +XULTreeAccessible::NativeRole() +{ + // No primary column means we're in a list. In fact, history and mail turn off + // the primary flag when switching to a flat view. + + nsIContent* child = nsTreeUtils::GetDescendantChild(mContent, nsGkAtoms::treechildren); + NS_ASSERTION(child, "tree without treechildren!"); + nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame()); + NS_ASSERTION(treeFrame, "xul tree accessible for tree without a frame!"); + if (!treeFrame) + return roles::LIST; + + RefPtr<nsTreeColumns> cols = treeFrame->Columns(); + nsCOMPtr<nsITreeColumn> primaryCol; + cols->GetPrimaryColumn(getter_AddRefs(primaryCol)); + + return primaryCol ? roles::OUTLINE : roles::LIST; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: Accessible implementation (DON'T put methods here) + +Accessible* +XULTreeAccessible::ChildAtPoint(int32_t aX, int32_t aY, + EWhichChildAtPoint aWhichChild) +{ + nsIFrame *frame = GetFrame(); + if (!frame) + return nullptr; + + nsPresContext *presContext = frame->PresContext(); + nsIPresShell* presShell = presContext->PresShell(); + + nsIFrame *rootFrame = presShell->GetRootFrame(); + NS_ENSURE_TRUE(rootFrame, nullptr); + + nsIntRect rootRect = rootFrame->GetScreenRect(); + + int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x; + int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y; + + int32_t row = -1; + nsCOMPtr<nsITreeColumn> column; + nsAutoString childEltUnused; + mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column), + childEltUnused); + + // If we failed to find tree cell for the given point then it might be + // tree columns. + if (row == -1 || !column) + return AccessibleWrap::ChildAtPoint(aX, aY, aWhichChild); + + Accessible* child = GetTreeItemAccessible(row); + if (aWhichChild == eDeepestChild && child) { + // Look for accessible cell for the found item accessible. + RefPtr<XULTreeItemAccessibleBase> treeitem = do_QueryObject(child); + + Accessible* cell = treeitem->GetCellAccessible(column); + if (cell) + child = cell; + } + + return child; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: SelectAccessible + +Accessible* +XULTreeAccessible::CurrentItem() +{ + if (!mTreeView) + return nullptr; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + int32_t currentIndex = -1; + selection->GetCurrentIndex(¤tIndex); + if (currentIndex >= 0) + return GetTreeItemAccessible(currentIndex); + } + + return nullptr; +} + +void +XULTreeAccessible::SetCurrentItem(Accessible* aItem) +{ + NS_ERROR("XULTreeAccessible::SetCurrentItem not implemented"); +} + +void +XULTreeAccessible::SelectedItems(nsTArray<Accessible*>* aItems) +{ + if (!mTreeView) + return; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (!selection) + return; + + int32_t rangeCount = 0; + selection->GetRangeCount(&rangeCount); + for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) { + int32_t firstIdx = 0, lastIdx = -1; + selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx); + for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) { + Accessible* item = GetTreeItemAccessible(rowIdx); + if (item) + aItems->AppendElement(item); + } + } +} + +uint32_t +XULTreeAccessible::SelectedItemCount() +{ + if (!mTreeView) + return 0; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + int32_t count = 0; + selection->GetCount(&count); + return count; + } + + return 0; +} + +bool +XULTreeAccessible::AddItemToSelection(uint32_t aIndex) +{ + if (!mTreeView) + return false; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + bool isSelected = false; + selection->IsSelected(aIndex, &isSelected); + if (!isSelected) + selection->ToggleSelect(aIndex); + + return true; + } + return false; +} + +bool +XULTreeAccessible::RemoveItemFromSelection(uint32_t aIndex) +{ + if (!mTreeView) + return false; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + bool isSelected = false; + selection->IsSelected(aIndex, &isSelected); + if (isSelected) + selection->ToggleSelect(aIndex); + + return true; + } + return false; +} + +bool +XULTreeAccessible::IsItemSelected(uint32_t aIndex) +{ + if (!mTreeView) + return false; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + bool isSelected = false; + selection->IsSelected(aIndex, &isSelected); + return isSelected; + } + return false; +} + +bool +XULTreeAccessible::UnselectAll() +{ + if (!mTreeView) + return false; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (!selection) + return false; + + selection->ClearSelection(); + return true; +} + +Accessible* +XULTreeAccessible::GetSelectedItem(uint32_t aIndex) +{ + if (!mTreeView) + return nullptr; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (!selection) + return nullptr; + + uint32_t selCount = 0; + int32_t rangeCount = 0; + selection->GetRangeCount(&rangeCount); + for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) { + int32_t firstIdx = 0, lastIdx = -1; + selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx); + for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) { + if (selCount == aIndex) + return GetTreeItemAccessible(rowIdx); + + selCount++; + } + } + + return nullptr; +} + +bool +XULTreeAccessible::SelectAll() +{ + // see if we are multiple select if so set ourselves as such + if (!mTreeView) + return false; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + bool single = false; + selection->GetSingle(&single); + if (!single) { + selection->SelectAll(); + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: Accessible implementation + +Accessible* +XULTreeAccessible::GetChildAt(uint32_t aIndex) const +{ + uint32_t childCount = Accessible::ChildCount(); + if (aIndex < childCount) + return Accessible::GetChildAt(aIndex); + + return GetTreeItemAccessible(aIndex - childCount); +} + +uint32_t +XULTreeAccessible::ChildCount() const +{ + // Tree's children count is row count + treecols count. + uint32_t childCount = Accessible::ChildCount(); + if (!mTreeView) + return childCount; + + int32_t rowCount = 0; + mTreeView->GetRowCount(&rowCount); + childCount += rowCount; + + return childCount; +} + +Relation +XULTreeAccessible::RelationByType(RelationType aType) +{ + if (aType == RelationType::NODE_PARENT_OF) { + if (mTreeView) + return Relation(new XULTreeItemIterator(this, mTreeView, -1)); + + return Relation(); + } + + return Accessible::RelationByType(aType); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: Widgets + +bool +XULTreeAccessible::IsWidget() const +{ + return true; +} + +bool +XULTreeAccessible::IsActiveWidget() const +{ + if (IsAutoCompletePopup()) { + nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = + do_QueryInterface(mContent->GetParent()); + + if (autoCompletePopupElm) { + bool isOpen = false; + autoCompletePopupElm->GetPopupOpen(&isOpen); + return isOpen; + } + } + return FocusMgr()->HasDOMFocus(mContent); +} + +bool +XULTreeAccessible::AreItemsOperable() const +{ + if (IsAutoCompletePopup()) { + nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = + do_QueryInterface(mContent->GetParent()); + + if (autoCompletePopupElm) { + bool isOpen = false; + autoCompletePopupElm->GetPopupOpen(&isOpen); + return isOpen; + } + } + return true; +} + +Accessible* +XULTreeAccessible::ContainerWidget() const +{ + if (IsAutoCompletePopup()) { + // This works for XUL autocompletes. It doesn't work for HTML forms + // autocomplete because of potential crossprocess calls (when autocomplete + // lives in content process while popup lives in chrome process). If that's + // a problem then rethink Widgets interface. + nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = + do_QueryInterface(mContent->GetParent()); + if (menuListElm) { + nsCOMPtr<nsIDOMNode> inputElm; + menuListElm->GetInputField(getter_AddRefs(inputElm)); + if (inputElm) { + nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm); + if (inputNode) { + Accessible* input = + mDoc->GetAccessible(inputNode); + return input ? input->ContainerWidget() : nullptr; + } + } + } + } + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: public implementation + +Accessible* +XULTreeAccessible::GetTreeItemAccessible(int32_t aRow) const +{ + if (aRow < 0 || IsDefunct() || !mTreeView) + return nullptr; + + int32_t rowCount = 0; + nsresult rv = mTreeView->GetRowCount(&rowCount); + if (NS_FAILED(rv) || aRow >= rowCount) + return nullptr; + + void *key = reinterpret_cast<void*>(intptr_t(aRow)); + Accessible* cachedTreeItem = mAccessibleCache.GetWeak(key); + if (cachedTreeItem) + return cachedTreeItem; + + RefPtr<Accessible> treeItem = CreateTreeItemAccessible(aRow); + if (treeItem) { + mAccessibleCache.Put(key, treeItem); + Document()->BindToDocument(treeItem, nullptr); + return treeItem; + } + + return nullptr; +} + +void +XULTreeAccessible::InvalidateCache(int32_t aRow, int32_t aCount) +{ + if (IsDefunct()) + return; + + if (!mTreeView) { + UnbindCacheEntriesFromDocument(mAccessibleCache); + return; + } + + // Do not invalidate the cache if rows have been inserted. + if (aCount > 0) + return; + + DocAccessible* document = Document(); + + // Fire destroy event for removed tree items and delete them from caches. + for (int32_t rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) { + + void* key = reinterpret_cast<void*>(intptr_t(rowIdx)); + Accessible* treeItem = mAccessibleCache.GetWeak(key); + + if (treeItem) { + RefPtr<AccEvent> event = + new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem); + nsEventShell::FireEvent(event); + + // Unbind from document, shutdown and remove from tree cache. + document->UnbindFromDocument(treeItem); + mAccessibleCache.Remove(key); + } + } + + // We dealt with removed tree items already however we may keep tree items + // having row indexes greater than row count. We should remove these dead tree + // items silently from caches. + int32_t newRowCount = 0; + nsresult rv = mTreeView->GetRowCount(&newRowCount); + if (NS_FAILED(rv)) + return; + + int32_t oldRowCount = newRowCount - aCount; + + for (int32_t rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) { + + void *key = reinterpret_cast<void*>(intptr_t(rowIdx)); + Accessible* treeItem = mAccessibleCache.GetWeak(key); + + if (treeItem) { + // Unbind from document, shutdown and remove from tree cache. + document->UnbindFromDocument(treeItem); + mAccessibleCache.Remove(key); + } + } +} + +void +XULTreeAccessible::TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow, + int32_t aStartCol, int32_t aEndCol) +{ + if (IsDefunct()) + return; + + if (!mTreeView) { + UnbindCacheEntriesFromDocument(mAccessibleCache); + return; + } + + int32_t endRow = aEndRow; + + nsresult rv; + if (endRow == -1) { + int32_t rowCount = 0; + rv = mTreeView->GetRowCount(&rowCount); + if (NS_FAILED(rv)) + return; + + endRow = rowCount - 1; + } + + nsCOMPtr<nsITreeColumns> treeColumns; + mTree->GetColumns(getter_AddRefs(treeColumns)); + if (!treeColumns) + return; + + int32_t endCol = aEndCol; + + if (endCol == -1) { + int32_t colCount = 0; + rv = treeColumns->GetCount(&colCount); + if (NS_FAILED(rv)) + return; + + endCol = colCount - 1; + } + + for (int32_t rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) { + + void *key = reinterpret_cast<void*>(intptr_t(rowIdx)); + Accessible* accessible = mAccessibleCache.GetWeak(key); + + if (accessible) { + RefPtr<XULTreeItemAccessibleBase> treeitemAcc = do_QueryObject(accessible); + NS_ASSERTION(treeitemAcc, "Wrong accessible at the given key!"); + + treeitemAcc->RowInvalidated(aStartCol, endCol); + } + } +} + +void +XULTreeAccessible::TreeViewChanged(nsITreeView* aView) +{ + if (IsDefunct()) + return; + + // Fire reorder event on tree accessible on accessible tree (do not fire + // show/hide events on tree items because it can be expensive to fire them for + // each tree item. + RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this); + Document()->FireDelayedEvent(reorderEvent); + + // Clear cache. + UnbindCacheEntriesFromDocument(mAccessibleCache); + + mTreeView = aView; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeAccessible: protected implementation + +already_AddRefed<Accessible> +XULTreeAccessible::CreateTreeItemAccessible(int32_t aRow) const +{ + RefPtr<Accessible> accessible = + new XULTreeItemAccessible(mContent, mDoc, const_cast<XULTreeAccessible*>(this), + mTree, mTreeView, aRow); + + return accessible.forget(); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessibleBase +//////////////////////////////////////////////////////////////////////////////// + +XULTreeItemAccessibleBase:: + XULTreeItemAccessibleBase(nsIContent* aContent, DocAccessible* aDoc, + Accessible* aParent, nsITreeBoxObject* aTree, + nsITreeView* aTreeView, int32_t aRow) : + AccessibleWrap(aContent, aDoc), + mTree(aTree), mTreeView(aTreeView), mRow(aRow) +{ + mParent = aParent; + mStateFlags |= eSharedNode; +} + +XULTreeItemAccessibleBase::~XULTreeItemAccessibleBase() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessibleBase: nsISupports implementation + +NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase, Accessible, + mTree) + +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase) + NS_INTERFACE_TABLE_INHERITED(XULTreeItemAccessibleBase, + XULTreeItemAccessibleBase) +NS_INTERFACE_TABLE_TAIL_INHERITING(Accessible) +NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessibleBase, Accessible) +NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessibleBase, Accessible) + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessibleBase: Accessible + +Accessible* +XULTreeItemAccessibleBase::FocusedChild() +{ + return FocusMgr()->FocusedAccessible() == this ? this : nullptr; +} + +nsIntRect +XULTreeItemAccessibleBase::Bounds() const +{ + // Get x coordinate and width from treechildren element, get y coordinate and + // height from tree cell. + + nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree); + if (!boxObj) + return nsIntRect(); + + nsCOMPtr<nsITreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree); + + int32_t x = 0, y = 0, width = 0, height = 0; + nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyString(), + &x, &y, &width, &height); + if (NS_FAILED(rv)) + return nsIntRect(); + + boxObj->GetWidth(&width); + + int32_t tcX = 0, tcY = 0; + boxObj->GetScreenX(&tcX); + boxObj->GetScreenY(&tcY); + + x = tcX; + y += tcY; + + nsPresContext* presContext = mDoc->PresContext(); + return nsIntRect(presContext->CSSPixelsToDevPixels(x), + presContext->CSSPixelsToDevPixels(y), + presContext->CSSPixelsToDevPixels(width), + presContext->CSSPixelsToDevPixels(height)); +} + +void +XULTreeItemAccessibleBase::SetSelected(bool aSelect) +{ + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + bool isSelected = false; + selection->IsSelected(mRow, &isSelected); + if (isSelected != aSelect) + selection->ToggleSelect(mRow); + } +} + +void +XULTreeItemAccessibleBase::TakeFocus() +{ + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) + selection->SetCurrentIndex(mRow); + + // focus event will be fired here + Accessible::TakeFocus(); +} + +Relation +XULTreeItemAccessibleBase::RelationByType(RelationType aType) +{ + + switch (aType) { + case RelationType::NODE_CHILD_OF: { + int32_t parentIndex = -1; + if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex))) + return Relation(); + + if (parentIndex == -1) + return Relation(mParent); + + XULTreeAccessible* treeAcc = mParent->AsXULTree(); + return Relation(treeAcc->GetTreeItemAccessible(parentIndex)); + } + + case RelationType::NODE_PARENT_OF: { + bool isTrue = false; + if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue) + return Relation(); + + if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue) + return Relation(); + + XULTreeAccessible* tree = mParent->AsXULTree(); + return Relation(new XULTreeItemIterator(tree, mTreeView, mRow)); + } + + default: + return Relation(); + } +} + +uint8_t +XULTreeItemAccessibleBase::ActionCount() +{ + // "activate" action is available for all treeitems, "expand/collapse" action + // is avaible for treeitem which is container. + return IsExpandable() ? 2 : 1; +} + +void +XULTreeItemAccessibleBase::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click) { + aName.AssignLiteral("activate"); + return; + } + + if (aIndex == eAction_Expand && IsExpandable()) { + bool isContainerOpen = false; + mTreeView->IsContainerOpen(mRow, &isContainerOpen); + if (isContainerOpen) + aName.AssignLiteral("collapse"); + else + aName.AssignLiteral("expand"); + } +} + +bool +XULTreeItemAccessibleBase::DoAction(uint8_t aIndex) +{ + if (aIndex != eAction_Click && + (aIndex != eAction_Expand || !IsExpandable())) + return false; + + DoCommand(nullptr, aIndex); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessibleBase: Accessible implementation + +void +XULTreeItemAccessibleBase::Shutdown() +{ + mTree = nullptr; + mTreeView = nullptr; + mRow = -1; + + AccessibleWrap::Shutdown(); +} + +GroupPos +XULTreeItemAccessibleBase::GroupPosition() +{ + GroupPos groupPos; + + int32_t level; + nsresult rv = mTreeView->GetLevel(mRow, &level); + NS_ENSURE_SUCCESS(rv, groupPos); + + int32_t topCount = 1; + for (int32_t index = mRow - 1; index >= 0; index--) { + int32_t lvl = -1; + if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) { + if (lvl < level) + break; + + if (lvl == level) + topCount++; + } + } + + int32_t rowCount = 0; + rv = mTreeView->GetRowCount(&rowCount); + NS_ENSURE_SUCCESS(rv, groupPos); + + int32_t bottomCount = 0; + for (int32_t index = mRow + 1; index < rowCount; index++) { + int32_t lvl = -1; + if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) { + if (lvl < level) + break; + + if (lvl == level) + bottomCount++; + } + } + + groupPos.level = level + 1; + groupPos.setSize = topCount + bottomCount; + groupPos.posInSet = topCount; + + return groupPos; +} + +uint64_t +XULTreeItemAccessibleBase::NativeState() +{ + + // focusable and selectable states + uint64_t state = NativeInteractiveState(); + + // expanded/collapsed state + if (IsExpandable()) { + bool isContainerOpen; + mTreeView->IsContainerOpen(mRow, &isContainerOpen); + state |= isContainerOpen ? states::EXPANDED : states::COLLAPSED; + } + + // selected state + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + bool isSelected; + selection->IsSelected(mRow, &isSelected); + if (isSelected) + state |= states::SELECTED; + } + + // focused state + if (FocusMgr()->IsFocused(this)) + state |= states::FOCUSED; + + // invisible state + int32_t firstVisibleRow, lastVisibleRow; + mTree->GetFirstVisibleRow(&firstVisibleRow); + mTree->GetLastVisibleRow(&lastVisibleRow); + if (mRow < firstVisibleRow || mRow > lastVisibleRow) + state |= states::INVISIBLE; + + return state; +} + +uint64_t +XULTreeItemAccessibleBase::NativeInteractiveState() const +{ + return states::FOCUSABLE | states::SELECTABLE; +} + +int32_t +XULTreeItemAccessibleBase::IndexInParent() const +{ + return mParent ? mParent->ContentChildCount() + mRow : -1; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessibleBase: Widgets + +Accessible* +XULTreeItemAccessibleBase::ContainerWidget() const +{ + return mParent; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessibleBase: Accessible protected methods + +void +XULTreeItemAccessibleBase::DispatchClickEvent(nsIContent* aContent, + uint32_t aActionIndex) +{ + if (IsDefunct()) + return; + + nsCOMPtr<nsITreeColumns> columns; + mTree->GetColumns(getter_AddRefs(columns)); + if (!columns) + return; + + // Get column and pseudo element. + nsCOMPtr<nsITreeColumn> column; + nsAutoString pseudoElm; + + if (aActionIndex == eAction_Click) { + // Key column is visible and clickable. + columns->GetKeyColumn(getter_AddRefs(column)); + } else { + // Primary column contains a twisty we should click on. + columns->GetPrimaryColumn(getter_AddRefs(column)); + pseudoElm = NS_LITERAL_STRING("twisty"); + } + + if (column) + nsCoreUtils::DispatchClickEvent(mTree, mRow, column, pseudoElm); +} + +Accessible* +XULTreeItemAccessibleBase::GetSiblingAtOffset(int32_t aOffset, + nsresult* aError) const +{ + if (aError) + *aError = NS_OK; // fail peacefully + + return mParent->GetChildAt(IndexInParent() + aOffset); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessibleBase: protected implementation + +bool +XULTreeItemAccessibleBase::IsExpandable() +{ + + bool isContainer = false; + mTreeView->IsContainer(mRow, &isContainer); + if (isContainer) { + bool isEmpty = false; + mTreeView->IsContainerEmpty(mRow, &isEmpty); + if (!isEmpty) { + nsCOMPtr<nsITreeColumns> columns; + mTree->GetColumns(getter_AddRefs(columns)); + nsCOMPtr<nsITreeColumn> primaryColumn; + if (columns) { + columns->GetPrimaryColumn(getter_AddRefs(primaryColumn)); + if (primaryColumn && + !nsCoreUtils::IsColumnHidden(primaryColumn)) + return true; + } + } + } + + return false; +} + +void +XULTreeItemAccessibleBase::GetCellName(nsITreeColumn* aColumn, nsAString& aName) +{ + + mTreeView->GetCellText(mRow, aColumn, aName); + + // If there is still no name try the cell value: + // This is for graphical cells. We need tree/table view implementors to + // implement FooView::GetCellValue to return a meaningful string for cases + // where there is something shown in the cell (non-text) such as a star icon; + // in which case GetCellValue for that cell would return "starred" or + // "flagged" for example. + if (aName.IsEmpty()) + mTreeView->GetCellValue(mRow, aColumn, aName); +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTreeItemAccessible:: + XULTreeItemAccessible(nsIContent* aContent, DocAccessible* aDoc, + Accessible* aParent, nsITreeBoxObject* aTree, + nsITreeView* aTreeView, int32_t aRow) : + XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView, aRow) +{ + mStateFlags |= eNoKidsFromDOM; + mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree); + GetCellName(mColumn, mCachedName); +} + +XULTreeItemAccessible::~XULTreeItemAccessible() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessible: nsISupports implementation + +NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible, + XULTreeItemAccessibleBase, + mColumn) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible) +NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase) +NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase) +NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase) + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessible: nsIAccessible implementation + +ENameValueFlag +XULTreeItemAccessible::Name(nsString& aName) +{ + aName.Truncate(); + + GetCellName(mColumn, aName); + return eNameOK; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessible: Accessible implementation + +void +XULTreeItemAccessible::Shutdown() +{ + mColumn = nullptr; + XULTreeItemAccessibleBase::Shutdown(); +} + +role +XULTreeItemAccessible::NativeRole() +{ + nsCOMPtr<nsITreeColumns> columns; + mTree->GetColumns(getter_AddRefs(columns)); + if (!columns) { + NS_ERROR("No tree columns object in the tree!"); + return roles::NOTHING; + } + + nsCOMPtr<nsITreeColumn> primaryColumn; + columns->GetPrimaryColumn(getter_AddRefs(primaryColumn)); + + return primaryColumn ? roles::OUTLINEITEM : roles::LISTITEM; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeItemAccessible: XULTreeItemAccessibleBase implementation + +void +XULTreeItemAccessible::RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx) +{ + nsAutoString name; + Name(name); + + if (name != mCachedName) { + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this); + mCachedName = name; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeColumAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTreeColumAccessible:: + XULTreeColumAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULColumAccessible(aContent, aDoc) +{ +} + +Accessible* +XULTreeColumAccessible::GetSiblingAtOffset(int32_t aOffset, + nsresult* aError) const +{ + if (aOffset < 0) + return XULColumAccessible::GetSiblingAtOffset(aOffset, aError); + + if (aError) + *aError = NS_OK; // fail peacefully + + nsCOMPtr<nsITreeBoxObject> tree = nsCoreUtils::GetTreeBoxObject(mContent); + if (tree) { + nsCOMPtr<nsITreeView> treeView; + tree->GetView(getter_AddRefs(treeView)); + if (treeView) { + int32_t rowCount = 0; + treeView->GetRowCount(&rowCount); + if (rowCount > 0 && aOffset <= rowCount) { + XULTreeAccessible* treeAcc = Parent()->AsXULTree(); + + if (treeAcc) + return treeAcc->GetTreeItemAccessible(aOffset - 1); + } + } + } + + return nullptr; +} diff --git a/accessible/xul/XULTreeAccessible.h b/accessible/xul/XULTreeAccessible.h new file mode 100644 index 000000000..489541c58 --- /dev/null +++ b/accessible/xul/XULTreeAccessible.h @@ -0,0 +1,279 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_XULTreeAccessible_h__ +#define mozilla_a11y_XULTreeAccessible_h__ + +#include "nsITreeBoxObject.h" +#include "nsITreeView.h" +#include "nsITreeColumns.h" +#include "XULListboxAccessible.h" + +class nsTreeBodyFrame; + +namespace mozilla { +namespace a11y { + +class XULTreeGridCellAccessible; + +/* + * A class the represents the XUL Tree widget. + */ +const uint32_t kMaxTreeColumns = 100; +const uint32_t kDefaultTreeCacheLength = 128; + +/** + * Accessible class for XUL tree element. + */ + +class XULTreeAccessible : public AccessibleWrap +{ +public: + XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc, + nsTreeBodyFrame* aTreeframe); + + // nsISupports and cycle collection + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeAccessible, Accessible) + + // Accessible + virtual void Shutdown() override; + virtual void Value(nsString& aValue) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY, + EWhichChildAtPoint aWhichChild) override; + + virtual Accessible* GetChildAt(uint32_t aIndex) const override; + virtual uint32_t ChildCount() const override; + virtual Relation RelationByType(RelationType aType) override; + + // SelectAccessible + virtual void SelectedItems(nsTArray<Accessible*>* aItems) override; + virtual uint32_t SelectedItemCount() override; + virtual Accessible* GetSelectedItem(uint32_t aIndex) override; + virtual bool IsItemSelected(uint32_t aIndex) override; + virtual bool AddItemToSelection(uint32_t aIndex) override; + virtual bool RemoveItemFromSelection(uint32_t aIndex) override; + virtual bool SelectAll() override; + virtual bool UnselectAll() override; + + // Widgets + virtual bool IsWidget() const override; + virtual bool IsActiveWidget() const override; + virtual bool AreItemsOperable() const override; + virtual Accessible* CurrentItem() override; + virtual void SetCurrentItem(Accessible* aItem) override; + + virtual Accessible* ContainerWidget() const override; + + // XULTreeAccessible + + /** + * Return tree item accessible at the givem row. If accessible doesn't exist + * in the cache then create and cache it. + * + * @param aRow [in] the given row index + */ + Accessible* GetTreeItemAccessible(int32_t aRow) const; + + /** + * Invalidates the number of cached treeitem accessibles. + * + * @param aRow [in] row index the invalidation starts from + * @param aCount [in] the number of treeitem accessibles to invalidate, + * the number sign specifies whether rows have been + * inserted (plus) or removed (minus) + */ + void InvalidateCache(int32_t aRow, int32_t aCount); + + /** + * Fires name change events for invalidated area of tree. + * + * @param aStartRow [in] row index invalidation starts from + * @param aEndRow [in] row index invalidation ends, -1 means last row index + * @param aStartCol [in] column index invalidation starts from + * @param aEndCol [in] column index invalidation ends, -1 mens last column + * index + */ + void TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow, + int32_t aStartCol, int32_t aEndCol); + + /** + * Invalidates children created for previous tree view. + */ + void TreeViewChanged(nsITreeView* aView); + +protected: + virtual ~XULTreeAccessible(); + + /** + * Creates tree item accessible for the given row index. + */ + virtual already_AddRefed<Accessible> + CreateTreeItemAccessible(int32_t aRow) const; + + nsCOMPtr<nsITreeBoxObject> mTree; + nsITreeView* mTreeView; + mutable AccessibleHashtable mAccessibleCache; +}; + +/** + * Base class for tree item accessibles. + */ + +#define XULTREEITEMBASEACCESSIBLE_IMPL_CID \ +{ /* 1ab79ae7-766a-443c-940b-b1e6b0831dfc */ \ + 0x1ab79ae7, \ + 0x766a, \ + 0x443c, \ + { 0x94, 0x0b, 0xb1, 0xe6, 0xb0, 0x83, 0x1d, 0xfc } \ +} + +class XULTreeItemAccessibleBase : public AccessibleWrap +{ +public: + XULTreeItemAccessibleBase(nsIContent* aContent, DocAccessible* aDoc, + Accessible* aParent, nsITreeBoxObject* aTree, + nsITreeView* aTreeView, int32_t aRow); + + // nsISupports and cycle collection + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessibleBase, + AccessibleWrap) + + // Accessible + virtual void Shutdown() override; + virtual nsIntRect Bounds() const override; + virtual GroupPos GroupPosition() override; + virtual uint64_t NativeState() override; + virtual uint64_t NativeInteractiveState() const override; + virtual int32_t IndexInParent() const override; + virtual Relation RelationByType(RelationType aType) override; + virtual Accessible* FocusedChild() override; + virtual void SetSelected(bool aSelect) override; + virtual void TakeFocus() override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + + // Widgets + virtual Accessible* ContainerWidget() const override; + + // XULTreeItemAccessibleBase + NS_DECLARE_STATIC_IID_ACCESSOR(XULTREEITEMBASEACCESSIBLE_IMPL_CID) + + /** + * Return row index associated with the accessible. + */ + int32_t GetRowIndex() const { return mRow; } + + /** + * Return cell accessible for the given column. If XUL tree accessible is not + * accessible table then return null. + */ + virtual XULTreeGridCellAccessible* GetCellAccessible(nsITreeColumn* aColumn) const + { return nullptr; } + + /** + * Proccess row invalidation. Used to fires name change events. + */ + virtual void RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx) = 0; + +protected: + virtual ~XULTreeItemAccessibleBase(); + + enum { eAction_Click = 0, eAction_Expand = 1 }; + + // Accessible + virtual void DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex) override; + virtual Accessible* GetSiblingAtOffset(int32_t aOffset, + nsresult *aError = nullptr) const override; + + // XULTreeItemAccessibleBase + + /** + * Return true if the tree item accessible is expandable (contains subrows). + */ + bool IsExpandable(); + + /** + * Return name for cell at the given column. + */ + void GetCellName(nsITreeColumn* aColumn, nsAString& aName); + + nsCOMPtr<nsITreeBoxObject> mTree; + nsITreeView* mTreeView; + int32_t mRow; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(XULTreeItemAccessibleBase, + XULTREEITEMBASEACCESSIBLE_IMPL_CID) + + +/** + * Accessible class for items for XUL tree. + */ +class XULTreeItemAccessible : public XULTreeItemAccessibleBase +{ +public: + XULTreeItemAccessible(nsIContent* aContent, DocAccessible* aDoc, + Accessible* aParent, nsITreeBoxObject* aTree, + nsITreeView* aTreeView, int32_t aRow); + + // nsISupports and cycle collection + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessible, + XULTreeItemAccessibleBase) + + // Accessible + virtual void Shutdown() override; + virtual ENameValueFlag Name(nsString& aName) override; + virtual a11y::role NativeRole() override; + + // XULTreeItemAccessibleBase + virtual void RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx) override; + +protected: + virtual ~XULTreeItemAccessible(); + + // XULTreeItemAccessible + nsCOMPtr<nsITreeColumn> mColumn; + nsString mCachedName; +}; + + +/** + * Accessible class for columns element of XUL tree. + */ +class XULTreeColumAccessible : public XULColumAccessible +{ +public: + XULTreeColumAccessible(nsIContent* aContent, DocAccessible* aDoc); + +protected: + + // Accessible + virtual Accessible* GetSiblingAtOffset(int32_t aOffset, + nsresult* aError = nullptr) const + override; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Accessible downcasting method + +inline XULTreeAccessible* +Accessible::AsXULTree() +{ + return IsXULTree() ? static_cast<XULTreeAccessible*>(this) : nullptr; +} + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xul/XULTreeGridAccessible.cpp b/accessible/xul/XULTreeGridAccessible.cpp new file mode 100644 index 000000000..e9e3a0e8f --- /dev/null +++ b/accessible/xul/XULTreeGridAccessible.cpp @@ -0,0 +1,823 @@ +/* -*- 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 "XULTreeGridAccessibleWrap.h" + +#include "nsAccCache.h" +#include "nsAccessibilityService.h" +#include "nsAccUtils.h" +#include "DocAccessible.h" +#include "nsEventShell.h" +#include "Relation.h" +#include "Role.h" +#include "States.h" +#include "nsQueryObject.h" + +#include "nsIBoxObject.h" +#include "nsIMutableArray.h" +#include "nsIPersistentProperties2.h" +#include "nsITreeSelection.h" +#include "nsComponentManagerUtils.h" + +using namespace mozilla::a11y; + +XULTreeGridAccessible::~XULTreeGridAccessible() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridAccessible: Table + +uint32_t +XULTreeGridAccessible::ColCount() +{ + return nsCoreUtils::GetSensibleColumnCount(mTree); +} + +uint32_t +XULTreeGridAccessible::RowCount() +{ + if (!mTreeView) + return 0; + + int32_t rowCount = 0; + mTreeView->GetRowCount(&rowCount); + return rowCount >= 0 ? rowCount : 0; +} + +uint32_t +XULTreeGridAccessible::SelectedCellCount() +{ + return SelectedRowCount() * ColCount(); +} + +uint32_t +XULTreeGridAccessible::SelectedColCount() +{ + // If all the row has been selected, then all the columns are selected, + // because we can't select a column alone. + + uint32_t selectedRowCount = SelectedItemCount(); + return selectedRowCount > 0 && selectedRowCount == RowCount() ? ColCount() : 0; +} + +uint32_t +XULTreeGridAccessible::SelectedRowCount() +{ + return SelectedItemCount(); +} + +void +XULTreeGridAccessible::SelectedCells(nsTArray<Accessible*>* aCells) +{ + uint32_t colCount = ColCount(), rowCount = RowCount(); + + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { + if (IsRowSelected(rowIdx)) { + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { + Accessible* cell = CellAt(rowIdx, colIdx); + aCells->AppendElement(cell); + } + } + } +} + +void +XULTreeGridAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) +{ + uint32_t colCount = ColCount(), rowCount = RowCount(); + + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) + if (IsRowSelected(rowIdx)) + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) + aCells->AppendElement(rowIdx * colCount + colIdx); +} + +void +XULTreeGridAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) +{ + if (RowCount() != SelectedRowCount()) + return; + + uint32_t colCount = ColCount(); + aCols->SetCapacity(colCount); + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) + aCols->AppendElement(colIdx); +} + +void +XULTreeGridAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) +{ + uint32_t rowCount = RowCount(); + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) + if (IsRowSelected(rowIdx)) + aRows->AppendElement(rowIdx); +} + +Accessible* +XULTreeGridAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) +{ + Accessible* row = GetTreeItemAccessible(aRowIndex); + if (!row) + return nullptr; + + nsCOMPtr<nsITreeColumn> column = + nsCoreUtils::GetSensibleColumnAt(mTree, aColumnIndex); + if (!column) + return nullptr; + + RefPtr<XULTreeItemAccessibleBase> rowAcc = do_QueryObject(row); + if (!rowAcc) + return nullptr; + + return rowAcc->GetCellAccessible(column); +} + +void +XULTreeGridAccessible::ColDescription(uint32_t aColIdx, nsString& aDescription) +{ + aDescription.Truncate(); + + Accessible* treeColumns = Accessible::GetChildAt(0); + if (treeColumns) { + Accessible* treeColumnItem = treeColumns->GetChildAt(aColIdx); + if (treeColumnItem) + treeColumnItem->Name(aDescription); + } +} + +bool +XULTreeGridAccessible::IsColSelected(uint32_t aColIdx) +{ + // If all the row has been selected, then all the columns are selected. + // Because we can't select a column alone. + return SelectedItemCount() == RowCount(); +} + +bool +XULTreeGridAccessible::IsRowSelected(uint32_t aRowIdx) +{ + if (!mTreeView) + return false; + + nsCOMPtr<nsITreeSelection> selection; + nsresult rv = mTreeView->GetSelection(getter_AddRefs(selection)); + NS_ENSURE_SUCCESS(rv, false); + + bool isSelected = false; + selection->IsSelected(aRowIdx, &isSelected); + return isSelected; +} + +bool +XULTreeGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) +{ + return IsRowSelected(aRowIdx); +} + +void +XULTreeGridAccessible::SelectRow(uint32_t aRowIdx) +{ + if (!mTreeView) + return; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + NS_ASSERTION(selection, "GetSelection() Shouldn't fail!"); + + selection->Select(aRowIdx); +} + +void +XULTreeGridAccessible::UnselectRow(uint32_t aRowIdx) +{ + if (!mTreeView) + return; + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + + if (selection) + selection->ClearRange(aRowIdx, aRowIdx); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridAccessible: Accessible implementation + +role +XULTreeGridAccessible::NativeRole() +{ + nsCOMPtr<nsITreeColumns> treeColumns; + mTree->GetColumns(getter_AddRefs(treeColumns)); + if (!treeColumns) { + NS_ERROR("No treecolumns object for tree!"); + return roles::NOTHING; + } + + nsCOMPtr<nsITreeColumn> primaryColumn; + treeColumns->GetPrimaryColumn(getter_AddRefs(primaryColumn)); + + return primaryColumn ? roles::TREE_TABLE : roles::TABLE; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridAccessible: XULTreeAccessible implementation + +already_AddRefed<Accessible> +XULTreeGridAccessible::CreateTreeItemAccessible(int32_t aRow) const +{ + RefPtr<Accessible> accessible = + new XULTreeGridRowAccessible(mContent, mDoc, + const_cast<XULTreeGridAccessible*>(this), + mTree, mTreeView, aRow); + + return accessible.forget(); +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridRowAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTreeGridRowAccessible:: + XULTreeGridRowAccessible(nsIContent* aContent, DocAccessible* aDoc, + Accessible* aTreeAcc, nsITreeBoxObject* aTree, + nsITreeView* aTreeView, int32_t aRow) : + XULTreeItemAccessibleBase(aContent, aDoc, aTreeAcc, aTree, aTreeView, aRow), + mAccessibleCache(kDefaultTreeCacheLength) +{ + mGenericTypes |= eTableRow; + mStateFlags |= eNoKidsFromDOM; +} + +XULTreeGridRowAccessible::~XULTreeGridRowAccessible() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridRowAccessible: nsISupports and cycle collection implementation + +NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeGridRowAccessible, + XULTreeItemAccessibleBase, + mAccessibleCache) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeGridRowAccessible) +NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase) + +NS_IMPL_ADDREF_INHERITED(XULTreeGridRowAccessible, + XULTreeItemAccessibleBase) +NS_IMPL_RELEASE_INHERITED(XULTreeGridRowAccessible, + XULTreeItemAccessibleBase) + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridRowAccessible: Accessible implementation + +void +XULTreeGridRowAccessible::Shutdown() +{ + if (mDoc && !mDoc->IsDefunct()) { + UnbindCacheEntriesFromDocument(mAccessibleCache); + } + + XULTreeItemAccessibleBase::Shutdown(); +} + +role +XULTreeGridRowAccessible::NativeRole() +{ + return roles::ROW; +} + +ENameValueFlag +XULTreeGridRowAccessible::Name(nsString& aName) +{ + aName.Truncate(); + + // XXX: the row name sholdn't be a concatenation of cell names (bug 664384). + nsCOMPtr<nsITreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree); + while (column) { + if (!aName.IsEmpty()) + aName.Append(' '); + + nsAutoString cellName; + GetCellName(column, cellName); + aName.Append(cellName); + + column = nsCoreUtils::GetNextSensibleColumn(column); + } + + return eNameOK; +} + +Accessible* +XULTreeGridRowAccessible::ChildAtPoint(int32_t aX, int32_t aY, + EWhichChildAtPoint aWhichChild) +{ + nsIFrame *frame = GetFrame(); + if (!frame) + return nullptr; + + nsPresContext *presContext = frame->PresContext(); + nsIPresShell* presShell = presContext->PresShell(); + + nsIFrame *rootFrame = presShell->GetRootFrame(); + NS_ENSURE_TRUE(rootFrame, nullptr); + + nsIntRect rootRect = rootFrame->GetScreenRect(); + + int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x; + int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y; + + int32_t row = -1; + nsCOMPtr<nsITreeColumn> column; + nsAutoString childEltUnused; + mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column), + childEltUnused); + + // Return if we failed to find tree cell in the row for the given point. + if (row != mRow || !column) + return nullptr; + + return GetCellAccessible(column); +} + +Accessible* +XULTreeGridRowAccessible::GetChildAt(uint32_t aIndex) const +{ + if (IsDefunct()) + return nullptr; + + nsCOMPtr<nsITreeColumn> column = + nsCoreUtils::GetSensibleColumnAt(mTree, aIndex); + if (!column) + return nullptr; + + return GetCellAccessible(column); +} + +uint32_t +XULTreeGridRowAccessible::ChildCount() const +{ + return nsCoreUtils::GetSensibleColumnCount(mTree); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridRowAccessible: XULTreeItemAccessibleBase implementation + +XULTreeGridCellAccessible* +XULTreeGridRowAccessible::GetCellAccessible(nsITreeColumn* aColumn) const +{ + NS_PRECONDITION(aColumn, "No tree column!"); + + void* key = static_cast<void*>(aColumn); + XULTreeGridCellAccessible* cachedCell = mAccessibleCache.GetWeak(key); + if (cachedCell) + return cachedCell; + + RefPtr<XULTreeGridCellAccessible> cell = + new XULTreeGridCellAccessibleWrap(mContent, mDoc, + const_cast<XULTreeGridRowAccessible*>(this), + mTree, mTreeView, mRow, aColumn); + mAccessibleCache.Put(key, cell); + Document()->BindToDocument(cell, nullptr); + return cell; +} + +void +XULTreeGridRowAccessible::RowInvalidated(int32_t aStartColIdx, + int32_t aEndColIdx) +{ + nsCOMPtr<nsITreeColumns> treeColumns; + mTree->GetColumns(getter_AddRefs(treeColumns)); + if (!treeColumns) + return; + + bool nameChanged = false; + for (int32_t colIdx = aStartColIdx; colIdx <= aEndColIdx; ++colIdx) { + nsCOMPtr<nsITreeColumn> column; + treeColumns->GetColumnAt(colIdx, getter_AddRefs(column)); + if (column && !nsCoreUtils::IsColumnHidden(column)) { + XULTreeGridCellAccessible* cell = GetCellAccessible(column); + if (cell) + nameChanged |= cell->CellInvalidated(); + } + } + + if (nameChanged) + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this); + +} + + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULTreeGridCellAccessible:: + XULTreeGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc, + XULTreeGridRowAccessible* aRowAcc, + nsITreeBoxObject* aTree, nsITreeView* aTreeView, + int32_t aRow, nsITreeColumn* aColumn) : + LeafAccessible(aContent, aDoc), mTree(aTree), + mTreeView(aTreeView), mRow(aRow), mColumn(aColumn) +{ + mParent = aRowAcc; + mStateFlags |= eSharedNode; + mGenericTypes |= eTableCell; + + NS_ASSERTION(mTreeView, "mTreeView is null"); + + int16_t type = -1; + mColumn->GetType(&type); + if (type == nsITreeColumn::TYPE_CHECKBOX) + mTreeView->GetCellValue(mRow, mColumn, mCachedTextEquiv); + else + mTreeView->GetCellText(mRow, mColumn, mCachedTextEquiv); +} + +XULTreeGridCellAccessible::~XULTreeGridCellAccessible() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessible: nsISupports implementation + +NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeGridCellAccessible, LeafAccessible, + mTree, mColumn) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeGridCellAccessible) +NS_INTERFACE_MAP_END_INHERITING(LeafAccessible) +NS_IMPL_ADDREF_INHERITED(XULTreeGridCellAccessible, LeafAccessible) +NS_IMPL_RELEASE_INHERITED(XULTreeGridCellAccessible, LeafAccessible) + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessible: Accessible + +Accessible* +XULTreeGridCellAccessible::FocusedChild() +{ + return nullptr; +} + +ENameValueFlag +XULTreeGridCellAccessible::Name(nsString& aName) +{ + aName.Truncate(); + + if (!mTreeView) + return eNameOK; + + mTreeView->GetCellText(mRow, mColumn, aName); + + // If there is still no name try the cell value: + // This is for graphical cells. We need tree/table view implementors to implement + // FooView::GetCellValue to return a meaningful string for cases where there is + // something shown in the cell (non-text) such as a star icon; in which case + // GetCellValue for that cell would return "starred" or "flagged" for example. + if (aName.IsEmpty()) + mTreeView->GetCellValue(mRow, mColumn, aName); + + return eNameOK; +} + +nsIntRect +XULTreeGridCellAccessible::Bounds() const +{ + // Get bounds for tree cell and add x and y of treechildren element to + // x and y of the cell. + nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree); + if (!boxObj) + return nsIntRect(); + + int32_t x = 0, y = 0, width = 0, height = 0; + nsresult rv = mTree->GetCoordsForCellItem(mRow, mColumn, + NS_LITERAL_STRING("cell"), + &x, &y, &width, &height); + if (NS_FAILED(rv)) + return nsIntRect(); + + int32_t tcX = 0, tcY = 0; + boxObj->GetScreenX(&tcX); + boxObj->GetScreenY(&tcY); + x += tcX; + y += tcY; + + nsPresContext* presContext = mDoc->PresContext(); + return nsIntRect(presContext->CSSPixelsToDevPixels(x), + presContext->CSSPixelsToDevPixels(y), + presContext->CSSPixelsToDevPixels(width), + presContext->CSSPixelsToDevPixels(height)); +} + +uint8_t +XULTreeGridCellAccessible::ActionCount() +{ + bool isCycler = false; + mColumn->GetCycler(&isCycler); + if (isCycler) + return 1; + + int16_t type; + mColumn->GetType(&type); + if (type == nsITreeColumn::TYPE_CHECKBOX && IsEditable()) + return 1; + + return 0; +} + +void +XULTreeGridCellAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) +{ + aName.Truncate(); + + if (aIndex != eAction_Click || !mTreeView) + return; + + bool isCycler = false; + mColumn->GetCycler(&isCycler); + if (isCycler) { + aName.AssignLiteral("cycle"); + return; + } + + int16_t type = 0; + mColumn->GetType(&type); + if (type == nsITreeColumn::TYPE_CHECKBOX && IsEditable()) { + nsAutoString value; + mTreeView->GetCellValue(mRow, mColumn, value); + if (value.EqualsLiteral("true")) + aName.AssignLiteral("uncheck"); + else + aName.AssignLiteral("check"); + } +} + +bool +XULTreeGridCellAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != eAction_Click) + return false; + + bool isCycler = false; + mColumn->GetCycler(&isCycler); + if (isCycler) { + DoCommand(); + return true; + } + + int16_t type; + mColumn->GetType(&type); + if (type == nsITreeColumn::TYPE_CHECKBOX && IsEditable()) { + DoCommand(); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessible: TableCell + +TableAccessible* +XULTreeGridCellAccessible::Table() const +{ + Accessible* grandParent = mParent->Parent(); + if (grandParent) + return grandParent->AsTable(); + + return nullptr; +} + +uint32_t +XULTreeGridCellAccessible::ColIdx() const +{ + uint32_t colIdx = 0; + nsCOMPtr<nsITreeColumn> column = mColumn; + while ((column = nsCoreUtils::GetPreviousSensibleColumn(column))) + colIdx++; + + return colIdx; +} + +uint32_t +XULTreeGridCellAccessible::RowIdx() const +{ + return mRow; +} + +void +XULTreeGridCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aHeaderCells) +{ + nsCOMPtr<nsIDOMElement> columnElm; + mColumn->GetElement(getter_AddRefs(columnElm)); + + nsCOMPtr<nsIContent> columnContent(do_QueryInterface(columnElm)); + Accessible* headerCell = mDoc->GetAccessible(columnContent); + if (headerCell) + aHeaderCells->AppendElement(headerCell); +} + +bool +XULTreeGridCellAccessible::Selected() +{ + nsCOMPtr<nsITreeSelection> selection; + nsresult rv = mTreeView->GetSelection(getter_AddRefs(selection)); + NS_ENSURE_SUCCESS(rv, false); + + bool selected = false; + selection->IsSelected(mRow, &selected); + return selected; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessible: Accessible public implementation + +already_AddRefed<nsIPersistentProperties> +XULTreeGridCellAccessible::NativeAttributes() +{ + nsCOMPtr<nsIPersistentProperties> attributes = + do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID); + + // "table-cell-index" attribute + TableAccessible* table = Table(); + if (!table) + return attributes.forget(); + + nsAutoString stringIdx; + stringIdx.AppendInt(table->CellIndexAt(mRow, ColIdx())); + nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx); + + // "cycles" attribute + bool isCycler = false; + nsresult rv = mColumn->GetCycler(&isCycler); + if (NS_SUCCEEDED(rv) && isCycler) + nsAccUtils::SetAccAttr(attributes, nsGkAtoms::cycles, + NS_LITERAL_STRING("true")); + + return attributes.forget(); +} + +role +XULTreeGridCellAccessible::NativeRole() +{ + return roles::GRID_CELL; +} + +uint64_t +XULTreeGridCellAccessible::NativeState() +{ + if (!mTreeView) + return states::DEFUNCT; + + // selectable/selected state + uint64_t states = states::SELECTABLE; // keep in sync with NativeInteractiveState + + nsCOMPtr<nsITreeSelection> selection; + mTreeView->GetSelection(getter_AddRefs(selection)); + if (selection) { + bool isSelected = false; + selection->IsSelected(mRow, &isSelected); + if (isSelected) + states |= states::SELECTED; + } + + // checked state + int16_t type; + mColumn->GetType(&type); + if (type == nsITreeColumn::TYPE_CHECKBOX) { + states |= states::CHECKABLE; + nsAutoString checked; + mTreeView->GetCellValue(mRow, mColumn, checked); + if (checked.EqualsIgnoreCase("true")) + states |= states::CHECKED; + } + + return states; +} + +uint64_t +XULTreeGridCellAccessible::NativeInteractiveState() const +{ + return states::SELECTABLE; +} + +int32_t +XULTreeGridCellAccessible::IndexInParent() const +{ + return ColIdx(); +} + +Relation +XULTreeGridCellAccessible::RelationByType(RelationType aType) +{ + return Relation(); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessible: public implementation + +bool +XULTreeGridCellAccessible::CellInvalidated() +{ + + nsAutoString textEquiv; + + int16_t type; + mColumn->GetType(&type); + if (type == nsITreeColumn::TYPE_CHECKBOX) { + mTreeView->GetCellValue(mRow, mColumn, textEquiv); + if (mCachedTextEquiv != textEquiv) { + bool isEnabled = textEquiv.EqualsLiteral("true"); + RefPtr<AccEvent> accEvent = + new AccStateChangeEvent(this, states::CHECKED, isEnabled); + nsEventShell::FireEvent(accEvent); + + mCachedTextEquiv = textEquiv; + return true; + } + + return false; + } + + mTreeView->GetCellText(mRow, mColumn, textEquiv); + if (mCachedTextEquiv != textEquiv) { + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this); + mCachedTextEquiv = textEquiv; + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessible: Accessible protected implementation + +Accessible* +XULTreeGridCellAccessible::GetSiblingAtOffset(int32_t aOffset, + nsresult* aError) const +{ + if (aError) + *aError = NS_OK; // fail peacefully + + nsCOMPtr<nsITreeColumn> columnAtOffset(mColumn), column; + if (aOffset < 0) { + for (int32_t index = aOffset; index < 0 && columnAtOffset; index++) { + column = nsCoreUtils::GetPreviousSensibleColumn(columnAtOffset); + column.swap(columnAtOffset); + } + } else { + for (int32_t index = aOffset; index > 0 && columnAtOffset; index--) { + column = nsCoreUtils::GetNextSensibleColumn(columnAtOffset); + column.swap(columnAtOffset); + } + } + + if (!columnAtOffset) + return nullptr; + + RefPtr<XULTreeItemAccessibleBase> rowAcc = do_QueryObject(Parent()); + return rowAcc->GetCellAccessible(columnAtOffset); +} + +void +XULTreeGridCellAccessible::DispatchClickEvent(nsIContent* aContent, + uint32_t aActionIndex) +{ + if (IsDefunct()) + return; + + nsCoreUtils::DispatchClickEvent(mTree, mRow, mColumn); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessible: protected implementation + +bool +XULTreeGridCellAccessible::IsEditable() const +{ + + // XXX: logic corresponds to tree.xml, it's preferable to have interface + // method to check it. + bool isEditable = false; + nsresult rv = mTreeView->IsEditable(mRow, mColumn, &isEditable); + if (NS_FAILED(rv) || !isEditable) + return false; + + nsCOMPtr<nsIDOMElement> columnElm; + mColumn->GetElement(getter_AddRefs(columnElm)); + if (!columnElm) + return false; + + nsCOMPtr<nsIContent> columnContent(do_QueryInterface(columnElm)); + if (!columnContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::editable, + nsGkAtoms::_true, + eCaseMatters)) + return false; + + return mContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::editable, + nsGkAtoms::_true, eCaseMatters); +} diff --git a/accessible/xul/XULTreeGridAccessible.h b/accessible/xul/XULTreeGridAccessible.h new file mode 100644 index 000000000..6a6bd0606 --- /dev/null +++ b/accessible/xul/XULTreeGridAccessible.h @@ -0,0 +1,187 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_XULTreeGridAccessible_h__ +#define mozilla_a11y_XULTreeGridAccessible_h__ + +#include "XULTreeAccessible.h" +#include "TableAccessible.h" +#include "TableCellAccessible.h" +#include "xpcAccessibleTable.h" +#include "xpcAccessibleTableCell.h" + +namespace mozilla { +namespace a11y { + +class XULTreeGridCellAccessible; + +/** + * Represents accessible for XUL tree in the case when it has multiple columns. + */ +class XULTreeGridAccessible : public XULTreeAccessible, + public TableAccessible +{ +public: + XULTreeGridAccessible(nsIContent* aContent, DocAccessible* aDoc, + nsTreeBodyFrame* aTreeFrame) : + XULTreeAccessible(aContent, aDoc, aTreeFrame) + { mGenericTypes |= eTable; } + + // TableAccessible + virtual uint32_t ColCount() override; + virtual uint32_t RowCount() override; + virtual Accessible* CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) override; + virtual void ColDescription(uint32_t aColIdx, nsString& aDescription) override; + virtual bool IsColSelected(uint32_t aColIdx) override; + virtual bool IsRowSelected(uint32_t aRowIdx) override; + virtual bool IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) override; + virtual uint32_t SelectedCellCount() override; + virtual uint32_t SelectedColCount() override; + virtual uint32_t SelectedRowCount() override; + virtual void SelectedCells(nsTArray<Accessible*>* aCells) override; + virtual void SelectedCellIndices(nsTArray<uint32_t>* aCells) override; + virtual void SelectedColIndices(nsTArray<uint32_t>* aCols) override; + virtual void SelectedRowIndices(nsTArray<uint32_t>* aRows) override; + virtual void SelectRow(uint32_t aRowIdx) override; + virtual void UnselectRow(uint32_t aRowIdx) override; + virtual Accessible* AsAccessible() override { return this; } + + // Accessible + virtual TableAccessible* AsTable() override { return this; } + virtual a11y::role NativeRole() override; + +protected: + virtual ~XULTreeGridAccessible(); + + // XULTreeAccessible + virtual already_AddRefed<Accessible> + CreateTreeItemAccessible(int32_t aRow) const override; +}; + + +/** + * Represents accessible for XUL tree item in the case when XUL tree has + * multiple columns. + */ +class XULTreeGridRowAccessible final : public XULTreeItemAccessibleBase +{ +public: + using Accessible::GetChildAt; + + XULTreeGridRowAccessible(nsIContent* aContent, DocAccessible* aDoc, + Accessible* aParent, nsITreeBoxObject* aTree, + nsITreeView* aTreeView, int32_t aRow); + + // nsISupports and cycle collection + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeGridRowAccessible, + XULTreeItemAccessibleBase) + + // Accessible + virtual void Shutdown() override; + virtual a11y::role NativeRole() override; + virtual ENameValueFlag Name(nsString& aName) override; + virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY, + EWhichChildAtPoint aWhichChild) override; + + virtual Accessible* GetChildAt(uint32_t aIndex) const override; + virtual uint32_t ChildCount() const override; + + // XULTreeItemAccessibleBase + virtual XULTreeGridCellAccessible* GetCellAccessible(nsITreeColumn* aColumn) + const override final; + virtual void RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx) override; + +protected: + virtual ~XULTreeGridRowAccessible(); + + // XULTreeItemAccessibleBase + mutable nsRefPtrHashtable<nsPtrHashKey<const void>, XULTreeGridCellAccessible> + mAccessibleCache; +}; + + +/** + * Represents an accessible for XUL tree cell in the case when XUL tree has + * multiple columns. + */ + +class XULTreeGridCellAccessible : public LeafAccessible, + public TableCellAccessible +{ +public: + + XULTreeGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc, + XULTreeGridRowAccessible* aRowAcc, + nsITreeBoxObject* aTree, nsITreeView* aTreeView, + int32_t aRow, nsITreeColumn* aColumn); + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeGridCellAccessible, + LeafAccessible) + + // Accessible + virtual TableCellAccessible* AsTableCell() override { return this; } + virtual nsIntRect Bounds() const override; + virtual ENameValueFlag Name(nsString& aName) override; + virtual Accessible* FocusedChild() override; + virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override; + virtual int32_t IndexInParent() const override; + virtual Relation RelationByType(RelationType aType) override; + virtual a11y::role NativeRole() override; + virtual uint64_t NativeState() override; + virtual uint64_t NativeInteractiveState() const override; + + // ActionAccessible + virtual uint8_t ActionCount() override; + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool DoAction(uint8_t aIndex) override; + + // TableCellAccessible + virtual TableAccessible* Table() const override; + virtual uint32_t ColIdx() const override; + virtual uint32_t RowIdx() const override; + virtual void ColHeaderCells(nsTArray<Accessible*>* aHeaderCells) override; + virtual void RowHeaderCells(nsTArray<Accessible*>* aCells) override { } + virtual bool Selected() override; + + /** + * Fire name or state change event if the accessible text or value has been + * changed. + * @return true if name has changed + */ + bool CellInvalidated(); + +protected: + virtual ~XULTreeGridCellAccessible(); + + // Accessible + virtual Accessible* GetSiblingAtOffset(int32_t aOffset, + nsresult* aError = nullptr) const override; + virtual void DispatchClickEvent(nsIContent* aContent, uint32_t aActionIndex) override; + + // XULTreeGridCellAccessible + + /** + * Return true if value of cell can be modified. + */ + bool IsEditable() const; + + enum { eAction_Click = 0 }; + + nsCOMPtr<nsITreeBoxObject> mTree; + nsITreeView* mTreeView; + + int32_t mRow; + nsCOMPtr<nsITreeColumn> mColumn; + + nsString mCachedTextEquiv; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xul/moz.build b/accessible/xul/moz.build new file mode 100644 index 000000000..df159b274 --- /dev/null +++ b/accessible/xul/moz.build @@ -0,0 +1,55 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +UNIFIED_SOURCES += [ + 'XULAlertAccessible.cpp', + 'XULColorPickerAccessible.cpp', + 'XULComboboxAccessible.cpp', + 'XULElementAccessibles.cpp', + 'XULFormControlAccessible.cpp', + 'XULListboxAccessible.cpp', + 'XULMenuAccessible.cpp', + 'XULSelectControlAccessible.cpp', + 'XULSliderAccessible.cpp', + 'XULTabAccessible.cpp', + 'XULTreeAccessible.cpp', + 'XULTreeGridAccessible.cpp', +] + +LOCAL_INCLUDES += [ + '/accessible/base', + '/accessible/generic', + '/accessible/html', + '/accessible/xpcom', + '/layout/generic', + '/layout/xul', + '/layout/xul/tree', +] + +if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: + LOCAL_INCLUDES += [ + '/accessible/atk', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': + LOCAL_INCLUDES += [ + '/accessible/windows/ia2', + '/accessible/windows/msaa', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + LOCAL_INCLUDES += [ + '/accessible/mac', + ] +else: + LOCAL_INCLUDES += [ + '/accessible/other', + ] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] |