summaryrefslogtreecommitdiffstats
path: root/accessible/xul
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--accessible/xul/XULAlertAccessible.cpp44
-rw-r--r--accessible/xul/XULAlertAccessible.h40
-rw-r--r--accessible/xul/XULComboboxAccessible.cpp142
-rw-r--r--accessible/xul/XULComboboxAccessible.h43
-rw-r--r--accessible/xul/XULElementAccessibles.cpp221
-rw-r--r--accessible/xul/XULElementAccessibles.h108
-rw-r--r--accessible/xul/XULFormControlAccessible.cpp450
-rw-r--r--accessible/xul/XULFormControlAccessible.h186
-rw-r--r--accessible/xul/XULListboxAccessible.cpp506
-rw-r--r--accessible/xul/XULListboxAccessible.h139
-rw-r--r--accessible/xul/XULMenuAccessible.cpp484
-rw-r--r--accessible/xul/XULMenuAccessible.h113
-rw-r--r--accessible/xul/XULSelectControlAccessible.cpp253
-rw-r--r--accessible/xul/XULSelectControlAccessible.h47
-rw-r--r--accessible/xul/XULTabAccessible.cpp218
-rw-r--r--accessible/xul/XULTabAccessible.h98
-rw-r--r--accessible/xul/XULTreeAccessible.cpp1017
-rw-r--r--accessible/xul/XULTreeAccessible.h276
-rw-r--r--accessible/xul/XULTreeGridAccessible.cpp653
-rw-r--r--accessible/xul/XULTreeGridAccessible.h186
-rw-r--r--accessible/xul/moz.build56
21 files changed, 5280 insertions, 0 deletions
diff --git a/accessible/xul/XULAlertAccessible.cpp b/accessible/xul/XULAlertAccessible.cpp
new file mode 100644
index 0000000000..73d0c69752
--- /dev/null
+++ b/accessible/xul/XULAlertAccessible.cpp
@@ -0,0 +1,44 @@
+/* -*- 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 "LocalAccessible-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() {}
+
+role XULAlertAccessible::NativeRole() const { return roles::ALERT; }
+
+uint64_t XULAlertAccessible::NativeState() const {
+ return LocalAccessible::NativeState() | states::ALERT;
+}
+
+ENameValueFlag XULAlertAccessible::Name(nsString& aName) const {
+ // 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; }
+
+LocalAccessible* XULAlertAccessible::ContainerWidget() const { return nullptr; }
diff --git a/accessible/xul/XULAlertAccessible.h b/accessible/xul/XULAlertAccessible.h
new file mode 100644
index 0000000000..12594e987d
--- /dev/null
+++ b/accessible/xul/XULAlertAccessible.h
@@ -0,0 +1,40 @@
+/* -*- 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 {
+
+/**
+ * LocalAccessible for supporting XUL alerts.
+ */
+
+class XULAlertAccessible : public AccessibleWrap {
+ public:
+ XULAlertAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(XULAlertAccessible, AccessibleWrap)
+
+ // LocalAccessible
+ virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override;
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+
+ // Widgets
+ virtual bool IsWidget() const override;
+ virtual LocalAccessible* ContainerWidget() const override;
+
+ protected:
+ ~XULAlertAccessible();
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/xul/XULComboboxAccessible.cpp b/accessible/xul/XULComboboxAccessible.cpp
new file mode 100644
index 0000000000..a020e6f3cb
--- /dev/null
+++ b/accessible/xul/XULComboboxAccessible.cpp
@@ -0,0 +1,142 @@
+/* -*- 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 "LocalAccessible-inl.h"
+#include "nsAccessibilityService.h"
+#include "DocAccessible.h"
+#include "nsCoreUtils.h"
+#include "Role.h"
+#include "States.h"
+
+#include "mozilla/dom/Element.h"
+#include "nsIDOMXULMenuListElement.h"
+
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// XULComboboxAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULComboboxAccessible::XULComboboxAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {
+ mGenericTypes |= eCombobox;
+}
+
+role XULComboboxAccessible::NativeRole() const { return roles::COMBOBOX; }
+
+uint64_t XULComboboxAccessible::NativeState() const {
+ // 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 = LocalAccessible::NativeState();
+
+ nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
+ if (menuList) {
+ bool isOpen = false;
+ menuList->GetOpen(&isOpen);
+ if (isOpen) {
+ state |= states::EXPANDED;
+ } else {
+ state |= states::COLLAPSED;
+ }
+ }
+
+ return state | states::HASPOPUP;
+}
+
+bool XULComboboxAccessible::IsAcceptableChild(nsIContent* aContent) const {
+ return AccessibleWrap::IsAcceptableChild(aContent) && !aContent->IsText();
+}
+
+void XULComboboxAccessible::Description(nsString& aDescription) const {
+ aDescription.Truncate();
+ // Use description of currently focused option
+ nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
+ if (!menuListElm) return;
+
+ nsCOMPtr<dom::Element> focusedOptionItem;
+ menuListElm->GetSelectedItem(getter_AddRefs(focusedOptionItem));
+ if (focusedOptionItem && mDoc) {
+ LocalAccessible* focusedOptionAcc = mDoc->GetAccessible(focusedOptionItem);
+ if (focusedOptionAcc) focusedOptionAcc->Description(aDescription);
+ }
+}
+
+void XULComboboxAccessible::Value(nsString& aValue) const {
+ aValue.Truncate();
+
+ // The value is the option or text shown entered in the combobox.
+ nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
+ if (menuList) menuList->GetLabel(aValue);
+}
+
+bool XULComboboxAccessible::HasPrimaryAction() const { return true; }
+
+bool XULComboboxAccessible::DoAction(uint8_t aIndex) const {
+ if (aIndex != XULComboboxAccessible::eAction_Click) return false;
+
+ // Programmaticaly toggle the combo box.
+ nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
+ 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 = Elm()->AsXULMenuList();
+ if (!menuList) return;
+
+ bool isDroppedDown = false;
+ menuList->GetOpen(&isDroppedDown);
+ if (isDroppedDown) {
+ aName.AssignLiteral("close");
+ } else {
+ aName.AssignLiteral("open");
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widgets
+
+bool XULComboboxAccessible::IsActiveWidget() const {
+ if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
+ nsGkAtoms::_true, eIgnoreCase)) {
+ int32_t childCount = mChildren.Length();
+ for (int32_t idx = 0; idx < childCount; idx++) {
+ LocalAccessible* child = mChildren[idx];
+ if (child->Role() == roles::ENTRY) {
+ return FocusMgr()->HasDOMFocus(child->GetContent());
+ }
+ }
+ return false;
+ }
+
+ return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool XULComboboxAccessible::AreItemsOperable() const {
+ nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
+ 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 0000000000..92f909690e
--- /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);
+
+ // LocalAccessible
+ void Description(nsString& aDescription) const override;
+ void Value(nsString& aValue) const override;
+ a11y::role NativeRole() const override;
+ uint64_t NativeState() const override;
+ bool IsAcceptableChild(nsIContent*) const override;
+
+ // ActionAccessible
+ bool HasPrimaryAction() const override;
+ void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+ bool DoAction(uint8_t aIndex) const override;
+
+ // Widgets
+ bool IsActiveWidget() const override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY 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 0000000000..247891cacc
--- /dev/null
+++ b/accessible/xul/XULElementAccessibles.cpp
@@ -0,0 +1,221 @@
+/* -*- 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 "LocalAccessible-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 "nsNameSpaceManager.h"
+#include "nsNetUtil.h"
+#include "nsString.h"
+#include "nsTextBoxFrame.h"
+#include "nsXULElement.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();
+}
+
+void XULLabelAccessible::DispatchClickEvent(nsIContent* aContent,
+ uint32_t aActionIndex) const {
+ // Bug 1578140: For labels inside buttons, The base implementation of
+ // DispatchClickEvent doesn't fire a command event on the button.
+ RefPtr<nsXULElement> el = nsXULElement::FromNodeOrNull(aContent);
+ if (el) {
+ el->Click(mozilla::dom::CallerType::System);
+ }
+}
+
+ENameValueFlag XULLabelAccessible::NativeName(nsString& aName) const {
+ // 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 LocalAccessible::NativeName(aName);
+}
+
+role XULLabelAccessible::NativeRole() const { return roles::LABEL; }
+
+uint64_t XULLabelAccessible::NativeState() const {
+ // Labels and description have read only state
+ // They are not focusable or selectable
+ return HyperTextAccessibleWrap::NativeState() | states::READONLY;
+}
+
+Relation XULLabelAccessible::RelationByType(RelationType aType) const {
+ Relation rel = HyperTextAccessibleWrap::RelationByType(aType);
+
+ // The label for xul:groupbox is generated from the first xul:label
+ if (aType == RelationType::LABEL_FOR) {
+ LocalAccessible* parent = LocalParent();
+ if (parent && parent->Role() == roles::GROUPING &&
+ parent->LocalChildAt(0) == this) {
+ nsIContent* parentContent = parent->GetContent();
+ if (parentContent && parentContent->IsXULElement(nsGkAtoms::groupbox)) {
+ 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() const { return roles::TEXT_LEAF; }
+
+uint64_t XULLabelTextLeafAccessible::NativeState() const {
+ return TextLeafAccessible::NativeState() | states::READONLY;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTooltipAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULTooltipAccessible::XULTooltipAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : LeafAccessible(aContent, aDoc) {
+ mType = eXULTooltipType;
+}
+
+uint64_t XULTooltipAccessible::NativeState() const {
+ return LeafAccessible::NativeState() | states::READONLY;
+}
+
+role XULTooltipAccessible::NativeRole() const { return roles::TOOLTIP; }
+
+////////////////////////////////////////////////////////////////////////////////
+// XULLinkAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULLinkAccessible::XULLinkAccessible(nsIContent* aContent, DocAccessible* aDoc)
+ : XULLabelAccessible(aContent, aDoc) {}
+
+XULLinkAccessible::~XULLinkAccessible() {}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULLinkAccessible: LocalAccessible
+
+void XULLinkAccessible::Value(nsString& aValue) const {
+ aValue.Truncate();
+
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::href, aValue);
+}
+
+ENameValueFlag XULLinkAccessible::NativeName(nsString& aName) const {
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
+ if (!aName.IsEmpty()) return eNameOK;
+
+ nsTextEquivUtils::GetNameFromSubtree(this, aName);
+ return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
+}
+
+role XULLinkAccessible::NativeRole() const { return roles::LINK; }
+
+uint64_t XULLinkAccessible::NativeLinkState() const { return states::LINKED; }
+
+bool XULLinkAccessible::HasPrimaryAction() const { return true; }
+
+void XULLinkAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
+ aName.Truncate();
+
+ if (aIndex == eAction_Jump) aName.AssignLiteral("jump");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULLinkAccessible: HyperLinkAccessible
+
+bool XULLinkAccessible::IsLink() const {
+ // 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 (LocalAccessible::IsLink()) return LocalAccessible::StartOffset();
+ return IndexInParent();
+}
+
+uint32_t XULLinkAccessible::EndOffset() {
+ if (LocalAccessible::IsLink()) return LocalAccessible::EndOffset();
+ return IndexInParent() + 1;
+}
+
+already_AddRefed<nsIURI> XULLinkAccessible::AnchorURIAt(
+ uint32_t aAnchorIndex) const {
+ if (aAnchorIndex != 0) return nullptr;
+
+ nsAutoString href;
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
+
+ dom::Document* document = mContent->OwnerDoc();
+
+ nsCOMPtr<nsIURI> anchorURI;
+ NS_NewURI(getter_AddRefs(anchorURI), href,
+ document->GetDocumentCharacterSet(), mContent->GetBaseURI());
+
+ return anchorURI.forget();
+}
diff --git a/accessible/xul/XULElementAccessibles.h b/accessible/xul/XULElementAccessibles.h
new file mode 100644
index 0000000000..c45f7c4bf3
--- /dev/null
+++ b/accessible/xul/XULElementAccessibles.h
@@ -0,0 +1,108 @@
+/* -*- 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 "TextLeafAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+class XULLabelTextLeafAccessible;
+
+/**
+ * Used for XUL description and label elements.
+ */
+class XULLabelAccessible : public HyperTextAccessibleWrap {
+ public:
+ XULLabelAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual void Shutdown() override;
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+ virtual Relation RelationByType(RelationType aType) const override;
+
+ void UpdateLabelValue(const nsString& aValue);
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+ virtual void DispatchClickEvent(nsIContent* aContent,
+ uint32_t aActionIndex) const override;
+
+ private:
+ RefPtr<XULLabelTextLeafAccessible> mValueTextLeaf;
+};
+
+inline XULLabelAccessible* LocalAccessible::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 TextLeafAccessible {
+ public:
+ XULLabelTextLeafAccessible(nsIContent* aContent, DocAccessible* aDoc)
+ : TextLeafAccessible(aContent, aDoc) {
+ mStateFlags |= eSharedNode;
+ }
+
+ virtual ~XULLabelTextLeafAccessible() {}
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+};
+
+/**
+ * Used for XUL tooltip element.
+ */
+class XULTooltipAccessible : public LeafAccessible {
+ public:
+ XULTooltipAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+};
+
+class XULLinkAccessible : public XULLabelAccessible {
+ public:
+ XULLinkAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual void Value(nsString& aValue) const override;
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeLinkState() const override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+
+ // HyperLinkAccessible
+ virtual bool IsLink() const override;
+ virtual uint32_t StartOffset() override;
+ virtual uint32_t EndOffset() override;
+ virtual already_AddRefed<nsIURI> AnchorURIAt(
+ uint32_t aAnchorIndex) const override;
+
+ protected:
+ virtual ~XULLinkAccessible();
+
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const 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 0000000000..4f414e6b0c
--- /dev/null
+++ b/accessible/xul/XULFormControlAccessible.cpp
@@ -0,0 +1,450 @@
+/* -*- 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 "LocalAccessible-inl.h"
+#include "HTMLFormControlAccessible.h"
+#include "nsAccUtils.h"
+#include "DocAccessible.h"
+#include "Relation.h"
+#include "Role.h"
+#include "States.h"
+#include "TreeWalker.h"
+#include "XULMenuAccessible.h"
+
+#include "nsIDOMXULButtonElement.h"
+#include "nsIDOMXULMenuListElement.h"
+#include "nsIDOMXULRadioGroupElement.h"
+#include "nsIDOMXULSelectCntrlItemEl.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
+
+////////////////////////////////////////////////////////////////////////////////
+// XULButtonAccessible: nsIAccessible
+
+bool XULButtonAccessible::HasPrimaryAction() const { return true; }
+
+void XULButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
+ if (aIndex == eAction_Click) aName.AssignLiteral("press");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULButtonAccessible: LocalAccessible
+
+role XULButtonAccessible::NativeRole() const {
+ // Buttons can be checked; they simply appear pressed in rather than checked.
+ // In this case, we must expose them as toggle buttons.
+ nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
+ if (xulButtonElement) {
+ nsAutoString type;
+ xulButtonElement->GetType(type);
+ if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
+ return roles::TOGGLE_BUTTON;
+ }
+ }
+ return roles::PUSHBUTTON;
+}
+
+uint64_t XULButtonAccessible::NativeState() const {
+ // Possible states: focused, focusable, unavailable(disabled).
+
+ // get focus and disable status from base class
+ uint64_t state = LocalAccessible::NativeState();
+
+ nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
+ if (xulButtonElement) {
+ // Some buttons can have their checked state set without being of type
+ // checkbox or radio. Expose the pressed state unconditionally.
+ bool checked = false;
+ xulButtonElement->GetChecked(&checked);
+ if (checked) {
+ state |= states::PRESSED;
+ }
+ }
+
+ if (ContainsMenu()) state |= states::HASPOPUP;
+
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_default)) {
+ state |= states::DEFAULT;
+ }
+
+ return state;
+}
+
+bool XULButtonAccessible::AttributeChangesState(nsAtom* aAttribute) {
+ if (aAttribute == nsGkAtoms::checked) {
+ return true;
+ }
+ return AccessibleWrap::AttributeChangesState(aAttribute);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULButtonAccessible: Widgets
+
+bool XULButtonAccessible::IsWidget() const { return true; }
+
+bool XULButtonAccessible::IsActiveWidget() const {
+ return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool XULButtonAccessible::AreItemsOperable() const {
+ if (IsMenuButton()) {
+ LocalAccessible* menuPopup = mChildren.SafeElementAt(0, nullptr);
+ if (menuPopup) {
+ nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
+ return menuPopupFrame->IsOpen();
+ }
+ }
+ return false; // no items
+}
+
+bool XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
+ // In general XUL buttons should not have accessible children. However:
+ return
+ // menu buttons can have popup accessibles (@type="menu" or
+ // columnpicker).
+ aEl->IsXULElement(nsGkAtoms::menupopup) ||
+ aEl->IsXULElement(nsGkAtoms::popup) ||
+ // A XUL button can be labelled by a direct child text node, so we need to
+ // allow that as a child so it will be picked up when computing name from
+ // subtree.
+ (aEl->IsText() && aEl->GetParent() == mContent);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULButtonAccessible protected
+
+bool XULButtonAccessible::ContainsMenu() const {
+ return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::menu, eCaseMatters);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULDropmarkerAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULDropmarkerAccessible::XULDropmarkerAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : LeafAccessible(aContent, aDoc) {}
+
+bool XULDropmarkerAccessible::HasPrimaryAction() const { return true; }
+
+bool XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const {
+ bool isOpen = false;
+
+ nsIContent* parent = mContent->GetFlattenedTreeParent();
+
+ while (parent) {
+ nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
+ parent->AsElement()->AsXULButton();
+ if (parentButtonElement) {
+ parentButtonElement->GetOpen(&isOpen);
+ if (aToggleOpen) parentButtonElement->SetOpen(!isOpen);
+ return isOpen;
+ }
+
+ nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
+ parent->AsElement()->AsXULMenuList();
+ 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) const {
+ if (index == eAction_Click) {
+ DropmarkerOpen(true); // Reverse the open attribute
+ return true;
+ }
+ return false;
+}
+
+role XULDropmarkerAccessible::NativeRole() const { return roles::PUSHBUTTON; }
+
+uint64_t XULDropmarkerAccessible::NativeState() const {
+ return DropmarkerOpen(false) ? states::PRESSED : 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULGroupboxAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULGroupboxAccessible::XULGroupboxAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {}
+
+role XULGroupboxAccessible::NativeRole() const { return roles::GROUPING; }
+
+ENameValueFlag XULGroupboxAccessible::NativeName(nsString& aName) const {
+ // XXX: we use the first related accessible only.
+ LocalAccessible* label =
+ RelationByType(RelationType::LABELLED_BY).LocalNext();
+ if (label) return label->Name(aName);
+
+ return eNameOK;
+}
+
+Relation XULGroupboxAccessible::RelationByType(RelationType aType) const {
+ Relation rel = AccessibleWrap::RelationByType(aType);
+
+ // The label for xul:groupbox is generated from the first xul:label
+ if (aType == RelationType::LABELLED_BY && ChildCount() > 0) {
+ LocalAccessible* childAcc = LocalChildAt(0);
+ if (childAcc->Role() == roles::LABEL &&
+ childAcc->GetContent()->IsXULElement(nsGkAtoms::label)) {
+ rel.AppendTarget(childAcc);
+ }
+ }
+
+ return rel;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULRadioButtonAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULRadioButtonAccessible::XULRadioButtonAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : RadioButtonAccessible(aContent, aDoc) {}
+
+uint64_t XULRadioButtonAccessible::NativeState() const {
+ uint64_t state = LeafAccessible::NativeState();
+ state |= states::CHECKABLE;
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
+ Elm()->AsXULSelectControlItem();
+ 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
+
+LocalAccessible* 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() const { 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; }
+
+LocalAccessible* XULRadioGroupAccessible::CurrentItem() const {
+ if (!mSelectControl) {
+ return nullptr;
+ }
+
+ RefPtr<dom::Element> currentItemElm;
+ nsCOMPtr<nsIDOMXULRadioGroupElement> group =
+ mSelectControl->AsXULRadioGroup();
+ if (group) {
+ group->GetFocusedItem(getter_AddRefs(currentItemElm));
+ }
+
+ if (currentItemElm) {
+ DocAccessible* document = Document();
+ if (document) {
+ return document->GetAccessible(currentItemElm);
+ }
+ }
+
+ return nullptr;
+}
+
+void XULRadioGroupAccessible::SetCurrentItem(const LocalAccessible* aItem) {
+ if (!mSelectControl) {
+ return;
+ }
+
+ nsCOMPtr<dom::Element> itemElm = aItem->Elm();
+ nsCOMPtr<nsIDOMXULRadioGroupElement> group =
+ mSelectControl->AsXULRadioGroup();
+ if (group) {
+ group->SetFocusedItem(itemElm);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULStatusBarAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULStatusBarAccessible::XULStatusBarAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {}
+
+role XULStatusBarAccessible::NativeRole() const { return roles::STATUSBAR; }
+
+////////////////////////////////////////////////////////////////////////////////
+// XULToolbarButtonAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULToolbarButtonAccessible::XULToolbarButtonAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : XULButtonAccessible(aContent, aDoc) {}
+
+void XULToolbarButtonAccessible::GetPositionAndSetSize(int32_t* aPosInSet,
+ int32_t* aSetSize) {
+ int32_t setSize = 0;
+ int32_t posInSet = 0;
+
+ LocalAccessible* parent = LocalParent();
+ if (!parent) return;
+
+ uint32_t childCount = parent->ChildCount();
+ for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
+ LocalAccessible* child = parent->LocalChildAt(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(LocalAccessible* aAccessible) {
+ nsIContent* content = aAccessible->GetContent();
+ return content && content->IsAnyOfXULElements(nsGkAtoms::toolbarseparator,
+ nsGkAtoms::toolbarspacer,
+ nsGkAtoms::toolbarspring);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULToolbarButtonAccessible: Widgets
+
+bool XULToolbarButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
+ // In general XUL button has not accessible children. Nevertheless menu
+ // buttons can have popup accessibles (@type="menu" or columnpicker).
+ // Also: Toolbar buttons can have labels as children.
+ // But only if the label attribute is not present.
+ return aEl->IsXULElement(nsGkAtoms::menupopup) ||
+ aEl->IsXULElement(nsGkAtoms::popup) ||
+ (aEl->IsXULElement(nsGkAtoms::label) &&
+ !mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::label));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULToolbarAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULToolbarAccessible::XULToolbarAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {}
+
+role XULToolbarAccessible::NativeRole() const { return roles::TOOLBAR; }
+
+ENameValueFlag XULToolbarAccessible::NativeName(nsString& aName) const {
+ if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::toolbarname,
+ aName)) {
+ aName.CompressWhitespace();
+ }
+
+ return eNameOK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULToolbarAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULToolbarSeparatorAccessible::XULToolbarSeparatorAccessible(
+ nsIContent* aContent, DocAccessible* aDoc)
+ : LeafAccessible(aContent, aDoc) {}
+
+role XULToolbarSeparatorAccessible::NativeRole() const {
+ return roles::SEPARATOR;
+}
+
+uint64_t XULToolbarSeparatorAccessible::NativeState() const { return 0; }
diff --git a/accessible/xul/XULFormControlAccessible.h b/accessible/xul/XULFormControlAccessible.h
new file mode 100644
index 0000000000..e48b0249a2
--- /dev/null
+++ b/accessible/xul/XULFormControlAccessible.h
@@ -0,0 +1,186 @@
+/* -*- 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 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_INLINE_DECL_REFCOUNTING_INHERITED(XULButtonAccessible, AccessibleWrap)
+
+ // LocalAccessible
+ virtual mozilla::a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+ virtual bool AttributeChangesState(nsAtom* aAttribute) override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+
+ // Widgets
+ virtual bool IsWidget() const override;
+ virtual bool IsActiveWidget() const override;
+ virtual bool AreItemsOperable() const override;
+
+ virtual bool IsAcceptableChild(nsIContent* aEl) const override;
+
+ protected:
+ virtual ~XULButtonAccessible();
+
+ // XULButtonAccessible
+ bool ContainsMenu() const;
+};
+
+/**
+ * Used for XUL dropmarker element.
+ */
+class XULDropmarkerAccessible : public LeafAccessible {
+ public:
+ enum { eAction_Click = 0 };
+ XULDropmarkerAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual mozilla::a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+ virtual bool DoAction(uint8_t aIndex) const override;
+
+ private:
+ bool DropmarkerOpen(bool aToggleOpen) const;
+};
+
+/**
+ * Used for XUL groupbox element.
+ */
+class XULGroupboxAccessible final : public AccessibleWrap {
+ public:
+ XULGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual mozilla::a11y::role NativeRole() const override;
+ virtual Relation RelationByType(RelationType aType) const override;
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+};
+
+/**
+ * Used for XUL radio element (radio button).
+ */
+class XULRadioButtonAccessible : public RadioButtonAccessible {
+ public:
+ XULRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual uint64_t NativeState() const override;
+ virtual uint64_t NativeInteractiveState() const override;
+
+ // Widgets
+ virtual LocalAccessible* ContainerWidget() const override;
+};
+
+/**
+ * Used for XUL radiogroup element.
+ */
+class XULRadioGroupAccessible : public XULSelectControlAccessible {
+ public:
+ XULRadioGroupAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual mozilla::a11y::role NativeRole() const override;
+ virtual uint64_t NativeInteractiveState() const override;
+
+ // Widgets
+ virtual bool IsWidget() const override;
+ virtual bool IsActiveWidget() const override;
+ virtual bool AreItemsOperable() const override;
+ virtual LocalAccessible* CurrentItem() const override;
+ virtual void SetCurrentItem(const LocalAccessible* aItem) override;
+};
+
+/**
+ * Used for XUL statusbar element.
+ */
+class XULStatusBarAccessible : public AccessibleWrap {
+ public:
+ XULStatusBarAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual mozilla::a11y::role NativeRole() const override;
+};
+
+/**
+ * Used for XUL toolbarbutton element.
+ */
+class XULToolbarButtonAccessible : public XULButtonAccessible {
+ public:
+ XULToolbarButtonAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // nsXULToolbarButtonAccessible
+ static bool IsSeparator(LocalAccessible* aAccessible);
+
+ // Widgets
+ virtual bool IsAcceptableChild(nsIContent* aEl) const override;
+
+ protected:
+ // LocalAccessible
+ virtual void GetPositionAndSetSize(int32_t* aPosInSet,
+ int32_t* aSetSize) override;
+};
+
+/**
+ * Used for XUL toolbar element.
+ */
+class XULToolbarAccessible : public AccessibleWrap {
+ public:
+ XULToolbarAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual mozilla::a11y::role NativeRole() const override;
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+};
+
+/**
+ * Used for XUL toolbarseparator element.
+ */
+class XULToolbarSeparatorAccessible : public LeafAccessible {
+ public:
+ XULToolbarSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual mozilla::a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/xul/XULListboxAccessible.cpp b/accessible/xul/XULListboxAccessible.cpp
new file mode 100644
index 0000000000..6d2a9e92c0
--- /dev/null
+++ b/accessible/xul/XULListboxAccessible.cpp
@@ -0,0 +1,506 @@
+/* -*- 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 "LocalAccessible-inl.h"
+#include "nsAccessibilityService.h"
+#include "nsAccUtils.h"
+#include "DocAccessible.h"
+#include "Role.h"
+#include "States.h"
+
+#include "nsComponentManagerUtils.h"
+#include "nsIAutoCompletePopup.h"
+#include "nsIDOMXULMenuListElement.h"
+#include "nsIDOMXULMultSelectCntrlEl.h"
+#include "nsIDOMXULSelectCntrlItemEl.h"
+#include "nsINodeList.h"
+
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// XULColumAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULColumAccessible::XULColumAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {}
+
+role XULColumAccessible::NativeRole() const { return roles::LIST; }
+
+uint64_t XULColumAccessible::NativeState() const { return states::READONLY; }
+
+////////////////////////////////////////////////////////////////////////////////
+// XULColumnItemAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULColumnItemAccessible::XULColumnItemAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : LeafAccessible(aContent, aDoc) {}
+
+role XULColumnItemAccessible::NativeRole() const { return roles::COLUMNHEADER; }
+
+uint64_t XULColumnItemAccessible::NativeState() const {
+ return states::READONLY;
+}
+
+bool XULColumnItemAccessible::HasPrimaryAction() const { return true; }
+
+void XULColumnItemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
+ if (aIndex == eAction_Click) aName.AssignLiteral("click");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULListboxAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULListboxAccessible::XULListboxAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : XULSelectControlAccessible(aContent, aDoc) {
+ dom::Element* parentEl = mContent->GetParentElement();
+ if (parentEl) {
+ nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+ parentEl->AsAutoCompletePopup();
+ if (autoCompletePopupElm) mGenericTypes |= eAutoCompletePopup;
+ }
+
+ if (IsMulticolumn()) mGenericTypes |= eTable;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULListboxAccessible: LocalAccessible
+
+uint64_t XULListboxAccessible::NativeState() const {
+ // As a XULListboxAccessible we can have the following states:
+ // FOCUSED, READONLY, FOCUSABLE
+
+ // Get focus status from base class
+ uint64_t states = LocalAccessible::NativeState();
+
+ // see if we are multiple select if so set ourselves as such
+
+ if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::seltype,
+ nsGkAtoms::multiple, eCaseMatters)) {
+ states |= states::MULTISELECTABLE | states::EXTSELECTABLE;
+ }
+
+ return states;
+}
+
+role XULListboxAccessible::NativeRole() const {
+ // A richlistbox is used with the new autocomplete URL bar, and has a parent
+ // popup <panel>.
+ if (mContent->GetParent() &&
+ mContent->GetParent()->IsXULElement(nsGkAtoms::panel)) {
+ return roles::COMBOBOX_LIST;
+ }
+
+ return IsMulticolumn() ? roles::TABLE : roles::LISTBOX;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULListboxAccessible: Table
+
+uint32_t XULListboxAccessible::ColCount() const { return 0; }
+
+uint32_t XULListboxAccessible::RowCount() {
+ nsCOMPtr<nsIDOMXULSelectControlElement> element = Elm()->AsXULSelectControl();
+
+ uint32_t itemCount = 0;
+ if (element) element->GetItemCount(&itemCount);
+
+ return itemCount;
+}
+
+LocalAccessible* XULListboxAccessible::CellAt(uint32_t aRowIndex,
+ uint32_t aColumnIndex) {
+ nsCOMPtr<nsIDOMXULSelectControlElement> control = Elm()->AsXULSelectControl();
+ NS_ENSURE_TRUE(control, nullptr);
+
+ RefPtr<dom::Element> element;
+ control->GetItemAtIndex(aRowIndex, getter_AddRefs(element));
+ if (!element) return nullptr;
+
+ LocalAccessible* row = mDoc->GetAccessible(element);
+ NS_ENSURE_TRUE(row, nullptr);
+
+ return row->LocalChildAt(aColumnIndex);
+}
+
+bool XULListboxAccessible::IsColSelected(uint32_t aColIdx) {
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
+ Elm()->AsXULMultiSelectControl();
+ 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 = Elm()->AsXULSelectControl();
+ NS_ASSERTION(control, "Doesn't implement nsIDOMXULSelectControlElement.");
+
+ RefPtr<dom::Element> element;
+ nsresult rv = control->GetItemAtIndex(aRowIdx, getter_AddRefs(element));
+ NS_ENSURE_SUCCESS(rv, false);
+ if (!element) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
+ element->AsXULSelectControlItem();
+
+ 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 =
+ Elm()->AsXULMultiSelectControl();
+ NS_ASSERTION(control,
+ "Doesn't implement nsIDOMXULMultiSelectControlElement.");
+
+ nsCOMPtr<nsINodeList> selectedItems;
+ control->GetSelectedItems(getter_AddRefs(selectedItems));
+ if (!selectedItems) return 0;
+
+ uint32_t selectedItemsCount = selectedItems->Length();
+
+ return selectedItemsCount * ColCount();
+}
+
+uint32_t XULListboxAccessible::SelectedColCount() {
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
+ Elm()->AsXULMultiSelectControl();
+ 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 =
+ Elm()->AsXULMultiSelectControl();
+ 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 =
+ Elm()->AsXULMultiSelectControl();
+ NS_ASSERTION(control,
+ "Doesn't implement nsIDOMXULMultiSelectControlElement.");
+
+ nsCOMPtr<nsINodeList> selectedItems;
+ control->GetSelectedItems(getter_AddRefs(selectedItems));
+ if (!selectedItems) return;
+
+ uint32_t selectedItemsCount = selectedItems->Length();
+
+ for (uint32_t index = 0; index < selectedItemsCount; index++) {
+ nsIContent* itemContent = selectedItems->Item(index);
+ LocalAccessible* item = mDoc->GetAccessible(itemContent);
+
+ if (item) {
+ uint32_t cellCount = item->ChildCount();
+ for (uint32_t cellIdx = 0; cellIdx < cellCount; cellIdx++) {
+ LocalAccessible* cell = mChildren[cellIdx];
+ if (cell->Role() == roles::CELL) aCells->AppendElement(cell);
+ }
+ }
+ }
+}
+
+void XULListboxAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) {
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
+ Elm()->AsXULMultiSelectControl();
+ NS_ASSERTION(control,
+ "Doesn't implement nsIDOMXULMultiSelectControlElement.");
+
+ nsCOMPtr<nsINodeList> selectedItems;
+ control->GetSelectedItems(getter_AddRefs(selectedItems));
+ if (!selectedItems) return;
+
+ uint32_t selectedItemsCount = selectedItems->Length();
+
+ uint32_t colCount = ColCount();
+ aCells->SetCapacity(selectedItemsCount * colCount);
+ aCells->AppendElements(selectedItemsCount * colCount);
+
+ for (uint32_t selItemsIdx = 0, cellsIdx = 0; selItemsIdx < selectedItemsCount;
+ selItemsIdx++) {
+ nsIContent* itemContent = selectedItems->Item(selItemsIdx);
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
+ itemContent->AsElement()->AsXULSelectControlItem();
+ 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 =
+ Elm()->AsXULMultiSelectControl();
+ NS_ASSERTION(control,
+ "Doesn't implement nsIDOMXULMultiSelectControlElement.");
+
+ nsCOMPtr<nsINodeList> selectedItems;
+ control->GetSelectedItems(getter_AddRefs(selectedItems));
+ if (!selectedItems) return;
+
+ uint32_t rowCount = selectedItems->Length();
+
+ if (!rowCount) return;
+
+ aRows->SetCapacity(rowCount);
+ aRows->AppendElements(rowCount);
+
+ for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ nsIContent* itemContent = selectedItems->Item(rowIdx);
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
+ itemContent->AsElement()->AsXULSelectControlItem();
+
+ 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 =
+ Elm()->AsXULMultiSelectControl();
+ NS_ASSERTION(control,
+ "Doesn't implement nsIDOMXULMultiSelectControlElement.");
+
+ RefPtr<dom::Element> item;
+ control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
+ if (!item) {
+ return;
+ }
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+ item->AsXULSelectControlItem();
+ control->SelectItem(itemElm);
+}
+
+void XULListboxAccessible::UnselectRow(uint32_t aRowIdx) {
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
+ Elm()->AsXULMultiSelectControl();
+ NS_ASSERTION(control,
+ "Doesn't implement nsIDOMXULMultiSelectControlElement.");
+
+ RefPtr<dom::Element> item;
+ control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
+ if (!item) {
+ return;
+ }
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+ item->AsXULSelectControlItem();
+ control->RemoveItemFromSelection(itemElm);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULListboxAccessible: Widgets
+
+bool XULListboxAccessible::IsWidget() const { return true; }
+
+bool XULListboxAccessible::IsActiveWidget() const {
+ if (IsAutoCompletePopup()) {
+ nsIContent* parentContent = mContent->GetParent();
+ if (parentContent) {
+ nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+ parentContent->AsElement()->AsAutoCompletePopup();
+ if (autoCompletePopupElm) {
+ bool isOpen = false;
+ autoCompletePopupElm->GetPopupOpen(&isOpen);
+ return isOpen;
+ }
+ }
+ }
+ return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool XULListboxAccessible::AreItemsOperable() const {
+ if (IsAutoCompletePopup()) {
+ nsIContent* parentContent = mContent->GetParent();
+ if (parentContent) {
+ nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
+ parentContent->AsElement()->AsAutoCompletePopup();
+ if (autoCompletePopupElm) {
+ bool isOpen = false;
+ autoCompletePopupElm->GetPopupOpen(&isOpen);
+ return isOpen;
+ }
+ }
+ }
+ return true;
+}
+
+LocalAccessible* XULListboxAccessible::ContainerWidget() const {
+ if (IsAutoCompletePopup() && mContent->GetParent()) {
+ // 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 =
+ mContent->GetParent()->AsElement()->AsXULMenuList();
+ if (menuListElm) {
+ RefPtr<mozilla::dom::Element> inputElm;
+ menuListElm->GetInputField(getter_AddRefs(inputElm));
+ if (inputElm) {
+ LocalAccessible* input = mDoc->GetAccessible(inputElm);
+ return input ? input->ContainerWidget() : nullptr;
+ }
+ }
+ }
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULListitemAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULListitemAccessible::XULListitemAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : XULMenuitemAccessible(aContent, aDoc) {
+ mIsCheckbox = mContent->AsElement()->AttrValueIs(
+ kNameSpaceID_None, nsGkAtoms::type, nsGkAtoms::checkbox, eCaseMatters);
+ mType = eXULListItemType;
+}
+
+XULListitemAccessible::~XULListitemAccessible() {}
+
+LocalAccessible* XULListitemAccessible::GetListAccessible() const {
+ if (IsDefunct()) return nullptr;
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
+ Elm()->AsXULSelectControlItem();
+ if (!listItem) return nullptr;
+
+ RefPtr<dom::Element> listElement;
+ listItem->GetControl(getter_AddRefs(listElement));
+ if (!listElement) return nullptr;
+
+ return mDoc->GetAccessible(listElement);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULListitemAccessible LocalAccessible
+
+void XULListitemAccessible::Description(nsString& aDesc) const {
+ AccessibleWrap::Description(aDesc);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULListitemAccessible: LocalAccessible
+
+/**
+ * Get the name from GetXULName.
+ */
+ENameValueFlag XULListitemAccessible::NativeName(nsString& aName) const {
+ return LocalAccessible::NativeName(aName);
+}
+
+role XULListitemAccessible::NativeRole() const {
+ LocalAccessible* 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() const {
+ if (mIsCheckbox) return XULMenuitemAccessible::NativeState();
+
+ uint64_t states = NativeInteractiveState();
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
+ Elm()->AsXULSelectControlItem();
+ 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
+
+LocalAccessible* XULListitemAccessible::ContainerWidget() const {
+ return LocalParent();
+}
diff --git a/accessible/xul/XULListboxAccessible.h b/accessible/xul/XULListboxAccessible.h
new file mode 100644
index 0000000000..9a4d1b0dc3
--- /dev/null
+++ b/accessible/xul/XULListboxAccessible.h
@@ -0,0 +1,139 @@
+/* -*- 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 "XULMenuAccessible.h"
+#include "XULSelectControlAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * XULColumAccessible are accessible for list and tree columns elements
+ * (xul:treecols and xul:listheader).
+ */
+class XULColumAccessible : public AccessibleWrap {
+ public:
+ XULColumAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+};
+
+/**
+ * XULColumnItemAccessible are accessibles for list and tree column elements
+ * (xul:treecol).
+ */
+class XULColumnItemAccessible : public LeafAccessible {
+ public:
+ XULColumnItemAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) 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() const override;
+ virtual uint32_t RowCount() override;
+ virtual LocalAccessible* 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 LocalAccessible* AsAccessible() override { return this; }
+
+ // LocalAccessible
+ virtual TableAccessible* AsTable() override { return this; }
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+
+ // Widgets
+ virtual bool IsWidget() const override;
+ virtual bool IsActiveWidget() const override;
+ virtual bool AreItemsOperable() const override;
+
+ virtual LocalAccessible* ContainerWidget() const override;
+
+ protected:
+ virtual ~XULListboxAccessible() {}
+
+ bool IsMulticolumn() const { return ColCount() > 1; }
+};
+
+/**
+ * Listitems -- used in listboxes
+ */
+class XULListitemAccessible : public XULMenuitemAccessible {
+ public:
+ enum { eAction_Click = 0 };
+
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(XULListitemAccessible,
+ XULMenuitemAccessible)
+
+ XULListitemAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual void Description(nsString& aDesc) const override;
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+ virtual uint64_t NativeInteractiveState() const override;
+
+ // Actions
+ virtual void ActionNameAt(uint8_t index, nsAString& aName) override;
+
+ // Widgets
+ virtual LocalAccessible* ContainerWidget() const override;
+
+ protected:
+ virtual ~XULListitemAccessible();
+
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+
+ // XULListitemAccessible
+
+ /**
+ * Return listbox accessible for the listitem.
+ */
+ LocalAccessible* GetListAccessible() const;
+
+ private:
+ bool mIsCheckbox;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/xul/XULMenuAccessible.cpp b/accessible/xul/XULMenuAccessible.cpp
new file mode 100644
index 0000000000..0a15e03970
--- /dev/null
+++ b/accessible/xul/XULMenuAccessible.cpp
@@ -0,0 +1,484 @@
+/* -*- 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 "LocalAccessible-inl.h"
+#include "XULMenuParentElement.h"
+#include "XULPopupElement.h"
+#include "mozilla/Assertions.h"
+#include "nsAccessibilityService.h"
+#include "nsAccUtils.h"
+#include "DocAccessible.h"
+#include "Role.h"
+#include "States.h"
+#include "XULFormControlAccessible.h"
+
+#include "nsIContentInlines.h"
+#include "nsIDOMXULContainerElement.h"
+#include "nsIDOMXULSelectCntrlEl.h"
+#include "nsIDOMXULSelectCntrlItemEl.h"
+#include "nsIContent.h"
+#include "nsMenuBarFrame.h"
+#include "nsMenuPopupFrame.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/XULButtonElement.h"
+#include "mozilla/dom/KeyboardEventBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// XULMenuitemAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULMenuitemAccessible::XULMenuitemAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {}
+
+uint64_t XULMenuitemAccessible::NativeState() const {
+ uint64_t state = LocalAccessible::NativeState();
+
+ // Has Popup?
+ if (mContent->NodeInfo()->Equals(nsGkAtoms::menu, kNameSpaceID_XUL)) {
+ state |= states::HASPOPUP;
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::open)) {
+ state |= states::EXPANDED;
+ } else {
+ state |= states::COLLAPSED;
+ }
+ }
+
+ // Checkable/checked?
+ static dom::Element::AttrValuesArray strings[] = {
+ nsGkAtoms::radio, nsGkAtoms::checkbox, nullptr};
+
+ if (mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
+ strings, eCaseMatters) >= 0) {
+ // Checkable?
+ state |= states::CHECKABLE;
+
+ // Checked?
+ if (mContent->AsElement()->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 =
+ Elm()->AsXULSelectControlItem();
+ NS_ENSURE_TRUE(item, state);
+ item->GetSelected(&isSelected);
+
+ // Is collapsed?
+ bool isCollapsed = false;
+ LocalAccessible* parent = LocalParent();
+ 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
+ LocalAccessible* grandParent = parent->LocalParent();
+ if (!grandParent) return state;
+ NS_ASSERTION(grandParent->IsCombobox(),
+ "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.
+ auto* button = dom::XULButtonElement::FromNode(GetContent());
+ bool skipNavigatingDisabledMenuItem = true;
+ if (!button || !button->IsOnMenuBar()) {
+ skipNavigatingDisabledMenuItem = LookAndFeel::GetInt(
+ LookAndFeel::IntID::SkipNavigatingDisabledMenuItem);
+ }
+
+ if (skipNavigatingDisabledMenuItem) return states::UNAVAILABLE;
+
+ return states::UNAVAILABLE | states::FOCUSABLE | states::SELECTABLE;
+ }
+
+ return states::FOCUSABLE | states::SELECTABLE;
+}
+
+ENameValueFlag XULMenuitemAccessible::NativeName(nsString& aName) const {
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
+ return eNameOK;
+}
+
+void XULMenuitemAccessible::Description(nsString& aDescription) const {
+ mContent->AsElement()->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->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey,
+ accesskey);
+ if (accesskey.IsEmpty()) return KeyBinding();
+
+ uint32_t modifierKey = 0;
+
+ LocalAccessible* parentAcc = LocalParent();
+ 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 dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
+ modifierKey = KeyBinding::kControl;
+ break;
+ case dom::KeyboardEvent_Binding::DOM_VK_ALT:
+ modifierKey = KeyBinding::kAlt;
+ break;
+ case dom::KeyboardEvent_Binding::DOM_VK_META:
+ modifierKey = KeyBinding::kMeta;
+ break;
+ case dom::KeyboardEvent_Binding::DOM_VK_WIN:
+ modifierKey = KeyBinding::kOS;
+ break;
+ }
+ }
+ }
+
+ return KeyBinding(accesskey[0], modifierKey);
+}
+
+KeyBinding XULMenuitemAccessible::KeyboardShortcut() const {
+ nsAutoString keyElmId;
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyElmId);
+ if (keyElmId.IsEmpty()) return KeyBinding();
+
+ dom::Element* 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, /* aRadix = */ 10);
+ if (NS_FAILED(errorCode)) {
+ key = keyStr.ToInteger(&errorCode, /* aRadix = */ 16);
+ }
+ } else {
+ key = keyStr[0];
+ }
+
+ nsAutoString modifiersStr;
+ keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr);
+
+ uint32_t modifierMask = 0;
+ if (modifiersStr.Find(u"shift") != -1) modifierMask |= KeyBinding::kShift;
+ if (modifiersStr.Find(u"alt") != -1) modifierMask |= KeyBinding::kAlt;
+ if (modifiersStr.Find(u"meta") != -1) modifierMask |= KeyBinding::kMeta;
+ if (modifiersStr.Find(u"os") != -1) modifierMask |= KeyBinding::kOS;
+ if (modifiersStr.Find(u"control") != -1) modifierMask |= KeyBinding::kControl;
+ if (modifiersStr.Find(u"accel") != -1) {
+ modifierMask |= KeyBinding::AccelModifier();
+ }
+
+ return KeyBinding(key, modifierMask);
+}
+
+role XULMenuitemAccessible::NativeRole() const {
+ nsCOMPtr<nsIDOMXULContainerElement> xulContainer = Elm()->AsXULContainer();
+ if (xulContainer) return roles::PARENT_MENUITEM;
+
+ LocalAccessible* widget = ContainerWidget();
+ if (widget && widget->Role() == roles::COMBOBOX_LIST) {
+ return roles::COMBOBOX_OPTION;
+ }
+
+ if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::radio, eCaseMatters)) {
+ return roles::RADIO_MENU_ITEM;
+ }
+
+ if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::checkbox, eCaseMatters)) {
+ return roles::CHECK_MENU_ITEM;
+ }
+
+ return roles::MENUITEM;
+}
+
+int32_t XULMenuitemAccessible::GetLevel(bool aFast) const {
+ return nsAccUtils::GetLevelForXULContainerItem(mContent);
+}
+
+void XULMenuitemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
+ if (aIndex == eAction_Click) aName.AssignLiteral("click");
+}
+
+bool XULMenuitemAccessible::HasPrimaryAction() const { return true; }
+
+////////////////////////////////////////////////////////////////////////////////
+// XULMenuitemAccessible: Widgets
+
+bool XULMenuitemAccessible::IsActiveWidget() const {
+ // Parent menu item is a widget, it's active when its popup is open.
+ // Typically the <menupopup> is included in the document markup, and
+ // <menu> prepends content in front of it.
+ nsIContent* menuPopupContent = mContent->GetLastChild();
+ 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->GetLastChild();
+ if (menuPopupContent) {
+ nsMenuPopupFrame* menuPopupFrame =
+ do_QueryFrame(menuPopupContent->GetPrimaryFrame());
+ return menuPopupFrame && menuPopupFrame->IsOpen();
+ }
+ return false;
+}
+
+LocalAccessible* XULMenuitemAccessible::ContainerWidget() const {
+ if (auto* button = dom::XULButtonElement::FromNode(GetContent())) {
+ if (auto* popup = button->GetMenuParent()) {
+ // We use GetAccessibleOrContainer instead of just GetAccessible because
+ // we strip menupopups from the tree for ATK.
+ return mDoc->GetAccessibleOrContainer(popup);
+ }
+ }
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULMenuSeparatorAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULMenuSeparatorAccessible::XULMenuSeparatorAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : XULMenuitemAccessible(aContent, aDoc) {}
+
+uint64_t XULMenuSeparatorAccessible::NativeState() const {
+ // Isn't focusable, but can be offscreen/invisible -- only copy those states
+ return XULMenuitemAccessible::NativeState() &
+ (states::OFFSCREEN | states::INVISIBLE);
+}
+
+ENameValueFlag XULMenuSeparatorAccessible::NativeName(nsString& aName) const {
+ return eNameOK;
+}
+
+role XULMenuSeparatorAccessible::NativeRole() const { return roles::SEPARATOR; }
+
+bool XULMenuSeparatorAccessible::HasPrimaryAction() const { return false; }
+
+////////////////////////////////////////////////////////////////////////////////
+// XULMenupopupAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULMenupopupAccessible::XULMenupopupAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : XULSelectControlAccessible(aContent, aDoc) {
+ if (nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame())) {
+ if (menuPopupFrame->PopupType() == ePopupTypeMenu) {
+ mType = eMenuPopupType;
+ }
+ }
+
+ // May be the anonymous <menupopup> inside <menulist> (a combobox)
+ auto* parent = mContent->GetParentElement();
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ parent ? parent->AsXULSelectControl() : nullptr;
+ if (selectControl) {
+ mSelectControl = parent;
+ } else {
+ mSelectControl = nullptr;
+ mGenericTypes &= ~eSelect;
+ }
+}
+
+uint64_t XULMenupopupAccessible::NativeState() const {
+ uint64_t state = LocalAccessible::NativeState();
+
+#ifdef DEBUG
+ // We are onscreen if our parent is active
+ bool isActive =
+ mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::menuactive);
+ if (!isActive) {
+ LocalAccessible* parent = LocalParent();
+ if (parent) {
+ nsIContent* parentContent = parent->GetContent();
+ if (parentContent && parentContent->IsElement())
+ isActive = parentContent->AsElement()->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) const {
+ nsIContent* content = mContent;
+ while (content && aName.IsEmpty()) {
+ if (content->IsElement()) {
+ content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
+ }
+ content = content->GetFlattenedTreeParent();
+ }
+
+ return eNameOK;
+}
+
+role XULMenupopupAccessible::NativeRole() const {
+ nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
+ if (menuPopupFrame && menuPopupFrame->IsContextMenu()) {
+ return roles::MENUPOPUP;
+ }
+
+ if (mParent) {
+ if (mParent->IsCombobox()) {
+ return roles::COMBOBOX_LIST;
+ }
+ }
+
+ // If accessible is not bound to the tree (this happens while children are
+ // cached) return general role.
+ 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();
+}
+
+LocalAccessible* XULMenupopupAccessible::ContainerWidget() const {
+ DocAccessible* document = Document();
+
+ nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
+ MOZ_ASSERT(menuPopupFrame);
+ if (!menuPopupFrame) {
+ return nullptr;
+ }
+
+ auto* cur = dom::XULPopupElement::FromNode(GetContent());
+ while (cur) {
+ auto* menu = cur->GetContainingMenu();
+ if (!menu) {
+ // <panel> / <tooltip> / etc.
+ return nullptr;
+ }
+ dom::XULMenuParentElement* parent = menu->GetMenuParent();
+ if (!parent) {
+ LocalAccessible* menuPopup = document->GetAccessible(cur);
+ MOZ_ASSERT(menuPopup);
+ return menuPopup ? menuPopup->LocalParent() : nullptr;
+ }
+
+ if (parent->IsMenuBar()) {
+ return document->GetAccessible(parent);
+ }
+
+ cur = dom::XULPopupElement::FromNode(parent);
+ MOZ_ASSERT(cur, "Should be a popup");
+ }
+
+ MOZ_ASSERT_UNREACHABLE("How did we get out of the loop without returning?");
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULMenubarAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULMenubarAccessible::XULMenubarAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {}
+
+ENameValueFlag XULMenubarAccessible::NativeName(nsString& aName) const {
+ aName.AssignLiteral("Application");
+ return eNameOK;
+}
+
+role XULMenubarAccessible::NativeRole() const { return roles::MENUBAR; }
+
+////////////////////////////////////////////////////////////////////////////////
+// XULMenubarAccessible: Widgets
+
+bool XULMenubarAccessible::IsActiveWidget() const {
+ nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
+ return menuBarFrame && menuBarFrame->IsActive();
+}
+
+bool XULMenubarAccessible::AreItemsOperable() const { return true; }
+
+LocalAccessible* XULMenubarAccessible::CurrentItem() const {
+ auto* content = dom::XULMenuParentElement::FromNode(GetContent());
+ MOZ_ASSERT(content);
+ if (!content || !content->GetActiveMenuChild()) {
+ return nullptr;
+ }
+ return mDoc->GetAccessible(content->GetActiveMenuChild());
+}
+
+void XULMenubarAccessible::SetCurrentItem(const LocalAccessible* aItem) {
+ NS_ERROR("XULMenubarAccessible::SetCurrentItem not implemented");
+}
diff --git a/accessible/xul/XULMenuAccessible.h b/accessible/xul/XULMenuAccessible.h
new file mode 100644
index 0000000000..43d165d798
--- /dev/null
+++ b/accessible/xul/XULMenuAccessible.h
@@ -0,0 +1,113 @@
+/* -*- 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 "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);
+
+ // LocalAccessible
+ virtual void Description(nsString& aDescription) const override;
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+ virtual uint64_t NativeInteractiveState() const override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+ virtual KeyBinding AccessKey() const override;
+ virtual KeyBinding KeyboardShortcut() const override;
+
+ // Widgets
+ virtual bool IsActiveWidget() const override;
+ virtual bool AreItemsOperable() const override;
+ virtual LocalAccessible* ContainerWidget() const override;
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+ virtual int32_t GetLevel(bool aFast) const override;
+};
+
+/**
+ * Used for XUL menuseparator element.
+ */
+class XULMenuSeparatorAccessible : public XULMenuitemAccessible {
+ public:
+ XULMenuSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const override;
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+};
+
+/**
+ * Used for XUL menupopup and panel.
+ */
+class XULMenupopupAccessible : public XULSelectControlAccessible {
+ public:
+ XULMenupopupAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+
+ // Widgets
+ virtual bool IsWidget() const override;
+ virtual bool IsActiveWidget() const override;
+ virtual bool AreItemsOperable() const override;
+
+ virtual LocalAccessible* ContainerWidget() const override;
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+};
+
+/**
+ * Used for XUL menubar element.
+ */
+class XULMenubarAccessible : public AccessibleWrap {
+ public:
+ XULMenubarAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+
+ // Widget
+ virtual bool IsActiveWidget() const override;
+ virtual bool AreItemsOperable() const override;
+ virtual LocalAccessible* CurrentItem() const override;
+ virtual void SetCurrentItem(const LocalAccessible* aItem) override;
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/xul/XULSelectControlAccessible.cpp b/accessible/xul/XULSelectControlAccessible.cpp
new file mode 100644
index 0000000000..006d1daf89
--- /dev/null
+++ b/accessible/xul/XULSelectControlAccessible.cpp
@@ -0,0 +1,253 @@
+/* -*- 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 "nsIDOMXULSelectCntrlItemEl.h"
+#include "nsIDOMXULMultSelectCntrlEl.h"
+
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/KeyboardEventBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// XULSelectControlAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULSelectControlAccessible::XULSelectControlAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {
+ mGenericTypes |= eSelect;
+ mSelectControl = aContent->AsElement();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULSelectControlAccessible: LocalAccessible
+
+void XULSelectControlAccessible::Shutdown() {
+ mSelectControl = nullptr;
+ AccessibleWrap::Shutdown();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULSelectControlAccessible: SelectAccessible
+
+void XULSelectControlAccessible::SelectedItems(nsTArray<Accessible*>* aItems) {
+ // For XUL multi-select control
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> xulMultiSelect =
+ mSelectControl->AsXULMultiSelectControl();
+ if (xulMultiSelect) {
+ int32_t length = 0;
+ xulMultiSelect->GetSelectedCount(&length);
+ for (int32_t index = 0; index < length; index++) {
+ RefPtr<dom::Element> element;
+ xulMultiSelect->MultiGetSelectedItem(index, getter_AddRefs(element));
+ LocalAccessible* item = mDoc->GetAccessible(element);
+ if (item) aItems->AppendElement(item);
+ }
+ } else { // Single select?
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ mSelectControl->AsXULSelectControl();
+ RefPtr<dom::Element> element;
+ selectControl->GetSelectedItem(getter_AddRefs(element));
+ if (element) {
+ LocalAccessible* item = mDoc->GetAccessible(element);
+ if (item) aItems->AppendElement(item);
+ }
+ }
+}
+
+Accessible* XULSelectControlAccessible::GetSelectedItem(uint32_t aIndex) {
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+ mSelectControl->AsXULMultiSelectControl();
+
+ RefPtr<dom::Element> element;
+ if (multiSelectControl) {
+ multiSelectControl->MultiGetSelectedItem(aIndex, getter_AddRefs(element));
+ } else if (aIndex == 0) {
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ mSelectControl->AsXULSelectControl();
+ if (selectControl) {
+ selectControl->GetSelectedItem(getter_AddRefs(element));
+ }
+ }
+
+ return element && mDoc ? mDoc->GetAccessible(element) : nullptr;
+}
+
+uint32_t XULSelectControlAccessible::SelectedItemCount() {
+ // For XUL multi-select control
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+ mSelectControl->AsXULMultiSelectControl();
+ if (multiSelectControl) {
+ int32_t count = 0;
+ multiSelectControl->GetSelectedCount(&count);
+ return count;
+ }
+
+ // For XUL single-select control/menulist
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ mSelectControl->AsXULSelectControl();
+ if (selectControl) {
+ int32_t index = -1;
+ selectControl->GetSelectedIndex(&index);
+ return (index >= 0) ? 1 : 0;
+ }
+
+ return 0;
+}
+
+bool XULSelectControlAccessible::AddItemToSelection(uint32_t aIndex) {
+ LocalAccessible* item = LocalChildAt(aIndex);
+ if (!item || !item->GetContent()) return false;
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+ item->GetContent()->AsElement()->AsXULSelectControlItem();
+ if (!itemElm) return false;
+
+ bool isItemSelected = false;
+ itemElm->GetSelected(&isItemSelected);
+ if (isItemSelected) return true;
+
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+ mSelectControl->AsXULMultiSelectControl();
+
+ if (multiSelectControl) {
+ multiSelectControl->AddItemToSelection(itemElm);
+ } else {
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ mSelectControl->AsXULSelectControl();
+ if (selectControl) {
+ selectControl->SetSelectedItem(item->Elm());
+ }
+ }
+
+ return true;
+}
+
+bool XULSelectControlAccessible::RemoveItemFromSelection(uint32_t aIndex) {
+ LocalAccessible* item = LocalChildAt(aIndex);
+ if (!item || !item->GetContent()) return false;
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+ item->GetContent()->AsElement()->AsXULSelectControlItem();
+ if (!itemElm) return false;
+
+ bool isItemSelected = false;
+ itemElm->GetSelected(&isItemSelected);
+ if (!isItemSelected) return true;
+
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+ mSelectControl->AsXULMultiSelectControl();
+
+ if (multiSelectControl) {
+ multiSelectControl->RemoveItemFromSelection(itemElm);
+ } else {
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ mSelectControl->AsXULSelectControl();
+ if (selectControl) {
+ selectControl->SetSelectedItem(nullptr);
+ }
+ }
+
+ return true;
+}
+
+bool XULSelectControlAccessible::IsItemSelected(uint32_t aIndex) {
+ LocalAccessible* item = LocalChildAt(aIndex);
+ if (!item || !item->GetContent()) return false;
+
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+ item->GetContent()->AsElement()->AsXULSelectControlItem();
+ if (!itemElm) return false;
+
+ bool isItemSelected = false;
+ itemElm->GetSelected(&isItemSelected);
+ return isItemSelected;
+}
+
+bool XULSelectControlAccessible::UnselectAll() {
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+ mSelectControl->AsXULMultiSelectControl();
+ if (multiSelectControl) {
+ multiSelectControl->ClearSelection();
+ } else {
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ mSelectControl->AsXULSelectControl();
+ if (selectControl) {
+ selectControl->SetSelectedIndex(-1);
+ }
+ }
+
+ return true;
+}
+
+bool XULSelectControlAccessible::SelectAll() {
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+ mSelectControl->AsXULMultiSelectControl();
+ if (multiSelectControl) {
+ multiSelectControl->SelectAll();
+ return true;
+ }
+
+ // otherwise, don't support this method
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULSelectControlAccessible: Widgets
+
+LocalAccessible* XULSelectControlAccessible::CurrentItem() const {
+ // aria-activedescendant should override.
+ LocalAccessible* current = AccessibleWrap::CurrentItem();
+ if (current) {
+ return current;
+ }
+
+ if (!mSelectControl) return nullptr;
+
+ RefPtr<dom::Element> currentItemElm;
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+ mSelectControl->AsXULMultiSelectControl();
+ if (multiSelectControl) {
+ multiSelectControl->GetCurrentItem(getter_AddRefs(currentItemElm));
+ } else {
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ mSelectControl->AsXULSelectControl();
+ if (selectControl) {
+ selectControl->GetSelectedItem(getter_AddRefs(currentItemElm));
+ }
+ }
+
+ if (currentItemElm) {
+ DocAccessible* document = Document();
+ if (document) return document->GetAccessible(currentItemElm);
+ }
+
+ return nullptr;
+}
+
+void XULSelectControlAccessible::SetCurrentItem(const LocalAccessible* aItem) {
+ if (!mSelectControl) return;
+
+ nsCOMPtr<dom::Element> itemElm = aItem->Elm();
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+ itemElm->AsXULMultiSelectControl();
+ if (multiSelectControl) {
+ multiSelectControl->SetCurrentItem(itemElm);
+ } else {
+ nsCOMPtr<nsIDOMXULSelectControlElement> selectControl =
+ mSelectControl->AsXULSelectControl();
+ if (selectControl) {
+ selectControl->SetSelectedItem(itemElm);
+ }
+ }
+}
diff --git a/accessible/xul/XULSelectControlAccessible.h b/accessible/xul/XULSelectControlAccessible.h
new file mode 100644
index 0000000000..ae201c74b0
--- /dev/null
+++ b/accessible/xul/XULSelectControlAccessible.h
@@ -0,0 +1,47 @@
+/* -*- 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"
+
+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() {}
+
+ // LocalAccessible
+ 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 LocalAccessible* CurrentItem() const override;
+ virtual void SetCurrentItem(const LocalAccessible* aItem) override;
+
+ protected:
+ RefPtr<dom::Element> mSelectControl;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/xul/XULTabAccessible.cpp b/accessible/xul/XULTabAccessible.cpp
new file mode 100644
index 0000000000..36358b3960
--- /dev/null
+++ b/accessible/xul/XULTabAccessible.cpp
@@ -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/. */
+
+#include "XULTabAccessible.h"
+
+#include "ARIAMap.h"
+#include "nsAccUtils.h"
+#include "Relation.h"
+#include "Role.h"
+#include "States.h"
+
+// NOTE: alphabetically ordered
+#include "mozilla/dom/Document.h"
+#include "nsIDOMXULSelectCntrlItemEl.h"
+#include "nsIDOMXULRelatedElement.h"
+#include "nsXULElement.h"
+
+#include "mozilla/dom/BindingDeclarations.h"
+
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTabAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULTabAccessible::XULTabAccessible(nsIContent* aContent, DocAccessible* aDoc)
+ : HyperTextAccessibleWrap(aContent, aDoc) {}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTabAccessible: LocalAccessible
+
+bool XULTabAccessible::HasPrimaryAction() const { return true; }
+
+void XULTabAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
+ if (aIndex == eAction_Switch) aName.AssignLiteral("switch");
+}
+
+bool XULTabAccessible::DoAction(uint8_t index) const {
+ if (index == eAction_Switch) {
+ // XXXbz Could this just FromContent?
+ RefPtr<nsXULElement> tab = nsXULElement::FromNodeOrNull(mContent);
+ if (tab) {
+ tab->Click(mozilla::dom::CallerType::System);
+ return true;
+ }
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTabAccessible: LocalAccessible
+
+role XULTabAccessible::NativeRole() const { return roles::PAGETAB; }
+
+uint64_t XULTabAccessible::NativeState() const {
+ // Possible states: focused, focusable, unavailable(disabled), offscreen.
+
+ // get focus and disable status from base class
+ uint64_t state = AccessibleWrap::NativeState();
+
+ // Check whether the tab is selected and/or pinned
+ nsCOMPtr<nsIDOMXULSelectControlItemElement> tab =
+ Elm()->AsXULSelectControlItem();
+ if (tab) {
+ bool selected = false;
+ if (NS_SUCCEEDED(tab->GetSelected(&selected)) && selected) {
+ state |= states::SELECTED;
+ }
+
+ if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::pinned,
+ nsGkAtoms::_true, eCaseMatters)) {
+ state |= states::PINNED;
+ }
+ }
+
+ return state;
+}
+
+uint64_t XULTabAccessible::NativeInteractiveState() const {
+ uint64_t state = LocalAccessible::NativeInteractiveState();
+ return (state & states::UNAVAILABLE) ? state : state | states::SELECTABLE;
+}
+
+Relation XULTabAccessible::RelationByType(RelationType aType) const {
+ Relation rel = AccessibleWrap::RelationByType(aType);
+ if (aType != RelationType::LABEL_FOR) return rel;
+
+ // Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible.
+ ErrorResult rv;
+ nsIContent* parent = mContent->AsElement()->Closest("tabs"_ns, rv);
+ if (!parent) return rel;
+
+ nsCOMPtr<nsIDOMXULRelatedElement> tabsElm =
+ parent->AsElement()->AsXULRelated();
+ if (!tabsElm) return rel;
+
+ RefPtr<mozilla::dom::Element> tabpanelElement;
+ tabsElm->GetRelatedElement(GetNode(), getter_AddRefs(tabpanelElement));
+ if (!tabpanelElement) return rel;
+
+ rel.AppendTarget(mDoc, tabpanelElement);
+ return rel;
+}
+
+void XULTabAccessible::ApplyARIAState(uint64_t* aState) const {
+ HyperTextAccessibleWrap::ApplyARIAState(aState);
+ // XUL tab has an implicit ARIA role of tab, so support aria-selected.
+ // Don't use aria::MapToState because that will set the SELECTABLE state
+ // even if the tab is disabled.
+ if (nsAccUtils::IsARIASelected(this)) {
+ *aState |= states::SELECTED;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTabsAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULTabsAccessible::XULTabsAccessible(nsIContent* aContent, DocAccessible* aDoc)
+ : XULSelectControlAccessible(aContent, aDoc) {}
+
+role XULTabsAccessible::NativeRole() const { return roles::PAGETABLIST; }
+
+bool XULTabsAccessible::HasPrimaryAction() const { return false; }
+
+void XULTabsAccessible::Value(nsString& aValue) const { aValue.Truncate(); }
+
+ENameValueFlag XULTabsAccessible::NativeName(nsString& aName) const {
+ // no name
+ return eNameOK;
+}
+
+void XULTabsAccessible::ApplyARIAState(uint64_t* aState) const {
+ XULSelectControlAccessible::ApplyARIAState(aState);
+ // XUL tabs has an implicit ARIA role of tablist, so support
+ // aria-multiselectable.
+ MOZ_ASSERT(Elm());
+ aria::MapToState(aria::eARIAMultiSelectable, Elm(), aState);
+}
+
+// XUL tabs is a single selection control and doesn't allow ARIA selection.
+// However, if aria-multiselectable is used, it becomes a multiselectable
+// control, where both native and ARIA markup are used to set selection.
+// Therefore, if aria-multiselectable is set, use the base implementation of
+// the selection retrieval methods in order to support ARIA selection.
+// We don't bother overriding the selection setting methods because
+// current front-end code using XUL tabs doesn't support setting of
+// aria-selected by the a11y engine and we still want to be able to set the
+// primary selected item according to XUL.
+
+void XULTabsAccessible::SelectedItems(nsTArray<Accessible*>* aItems) {
+ if (nsAccUtils::IsARIAMultiSelectable(this)) {
+ AccessibleWrap::SelectedItems(aItems);
+ } else {
+ XULSelectControlAccessible::SelectedItems(aItems);
+ }
+}
+
+Accessible* XULTabsAccessible::GetSelectedItem(uint32_t aIndex) {
+ if (nsAccUtils::IsARIAMultiSelectable(this)) {
+ return AccessibleWrap::GetSelectedItem(aIndex);
+ }
+
+ return XULSelectControlAccessible::GetSelectedItem(aIndex);
+}
+
+uint32_t XULTabsAccessible::SelectedItemCount() {
+ if (nsAccUtils::IsARIAMultiSelectable(this)) {
+ return AccessibleWrap::SelectedItemCount();
+ }
+
+ return XULSelectControlAccessible::SelectedItemCount();
+}
+
+bool XULTabsAccessible::IsItemSelected(uint32_t aIndex) {
+ if (nsAccUtils::IsARIAMultiSelectable(this)) {
+ return AccessibleWrap::IsItemSelected(aIndex);
+ }
+
+ return XULSelectControlAccessible::IsItemSelected(aIndex);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTabpanelsAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+role XULTabpanelsAccessible::NativeRole() const { return roles::PANE; }
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTabpanelAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULTabpanelAccessible::XULTabpanelAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {}
+
+role XULTabpanelAccessible::NativeRole() const { return roles::PROPERTYPAGE; }
+
+Relation XULTabpanelAccessible::RelationByType(RelationType aType) const {
+ Relation rel = AccessibleWrap::RelationByType(aType);
+ if (aType != RelationType::LABELLED_BY) return rel;
+
+ // Expose 'LABELLED_BY' relation on tabpanel accessible for tab accessible.
+ if (!mContent->GetParent()) return rel;
+
+ nsCOMPtr<nsIDOMXULRelatedElement> tabpanelsElm =
+ mContent->GetParent()->AsElement()->AsXULRelated();
+ if (!tabpanelsElm) return rel;
+
+ RefPtr<mozilla::dom::Element> tabElement;
+ tabpanelsElm->GetRelatedElement(GetNode(), getter_AddRefs(tabElement));
+ if (!tabElement) return rel;
+
+ rel.AppendTarget(mDoc, tabElement);
+ return rel;
+}
diff --git a/accessible/xul/XULTabAccessible.h b/accessible/xul/XULTabAccessible.h
new file mode 100644
index 0000000000..5ab4138788
--- /dev/null
+++ b/accessible/xul/XULTabAccessible.h
@@ -0,0 +1,98 @@
+/* -*- 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 "HyperTextAccessibleWrap.h"
+#include "XULMenuAccessible.h"
+#include "XULSelectControlAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * An individual tab, xul:tab element.
+ */
+class XULTabAccessible : public HyperTextAccessibleWrap {
+ public:
+ enum { eAction_Switch = 0 };
+
+ XULTabAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+ virtual uint64_t NativeInteractiveState() const override;
+ virtual Relation RelationByType(RelationType aType) const override;
+ virtual void ApplyARIAState(uint64_t* aState) const override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+ virtual bool DoAction(uint8_t aIndex) const override;
+};
+
+/**
+ * A container of tab objects, xul:tabs element.
+ */
+class XULTabsAccessible : public XULSelectControlAccessible {
+ public:
+ XULTabsAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // LocalAccessible
+ virtual void Value(nsString& aValue) const override;
+ virtual a11y::role NativeRole() const override;
+ virtual void ApplyARIAState(uint64_t* aState) const override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const 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;
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+};
+
+/**
+ * A container of tab panels, xul:tabpanels element.
+ */
+class XULTabpanelsAccessible : public AccessibleWrap {
+ public:
+ XULTabpanelsAccessible(nsIContent* aContent, DocAccessible* aDoc)
+ : AccessibleWrap(aContent, aDoc) {
+ mType = eXULTabpanelsType;
+ }
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const 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);
+
+ // LocalAccessible
+ virtual a11y::role NativeRole() const override;
+ virtual Relation RelationByType(RelationType aType) const override;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/xul/XULTreeAccessible.cpp b/accessible/xul/XULTreeAccessible.cpp
new file mode 100644
index 0000000000..f68370d468
--- /dev/null
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -0,0 +1,1017 @@
+/* -*- 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 "LocalAccessible-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 "nsIAutoCompletePopup.h"
+#include "nsIDOMXULMenuListElement.h"
+#include "nsITreeSelection.h"
+#include "nsTreeBodyFrame.h"
+#include "nsTreeColumns.h"
+#include "nsTreeUtils.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/dom/XULTreeElementBinding.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::GetTree(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, LocalAccessible, mTree,
+ mAccessibleCache)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeAccessible)
+NS_INTERFACE_MAP_END_INHERITING(LocalAccessible)
+
+NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, LocalAccessible)
+NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, LocalAccessible)
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeAccessible: LocalAccessible implementation
+
+uint64_t XULTreeAccessible::NativeState() const {
+ // Get focus status from base class.
+ uint64_t state = LocalAccessible::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) const {
+ 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(&currentIndex);
+ if (currentIndex >= 0) {
+ RefPtr<nsTreeColumn> keyCol;
+
+ RefPtr<nsTreeColumns> cols = mTree->GetColumns();
+ if (cols) keyCol = cols->GetKeyColumn();
+
+ mTreeView->GetCellText(currentIndex, keyCol, aValue);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeAccessible: LocalAccessible implementation
+
+void XULTreeAccessible::Shutdown() {
+ if (mDoc && !mDoc->IsDefunct()) {
+ UnbindCacheEntriesFromDocument(mAccessibleCache);
+ }
+
+ mTree = nullptr;
+ mTreeView = nullptr;
+
+ AccessibleWrap::Shutdown();
+}
+
+role XULTreeAccessible::NativeRole() const {
+ // 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();
+ nsTreeColumn* primaryCol = cols->GetPrimaryColumn();
+
+ return primaryCol ? roles::OUTLINE : roles::LIST;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeAccessible: LocalAccessible implementation (DON'T put methods here)
+
+LocalAccessible* XULTreeAccessible::LocalChildAtPoint(
+ int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) {
+ nsIFrame* frame = GetFrame();
+ if (!frame) return nullptr;
+
+ nsPresContext* presContext = frame->PresContext();
+ PresShell* presShell = presContext->PresShell();
+
+ nsIFrame* rootFrame = presShell->GetRootFrame();
+ NS_ENSURE_TRUE(rootFrame, nullptr);
+
+ CSSIntRect rootRect = rootFrame->GetScreenRect();
+
+ int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.X();
+ int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.Y();
+
+ ErrorResult rv;
+ dom::TreeCellInfo cellInfo;
+ mTree->GetCellAt(clientX, clientY, cellInfo, rv);
+
+ // If we failed to find tree cell for the given point then it might be
+ // tree columns.
+ if (cellInfo.mRow == -1 || !cellInfo.mCol) {
+ return AccessibleWrap::LocalChildAtPoint(aX, aY, aWhichChild);
+ }
+
+ LocalAccessible* child = GetTreeItemAccessible(cellInfo.mRow);
+ if (aWhichChild == EWhichChildAtPoint::DeepestChild && child) {
+ // Look for accessible cell for the found item accessible.
+ RefPtr<XULTreeItemAccessibleBase> treeitem = do_QueryObject(child);
+
+ LocalAccessible* cell = treeitem->GetCellAccessible(cellInfo.mCol);
+ if (cell) child = cell;
+ }
+
+ return child;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeAccessible: SelectAccessible
+
+LocalAccessible* XULTreeAccessible::CurrentItem() const {
+ if (!mTreeView) return nullptr;
+
+ nsCOMPtr<nsITreeSelection> selection;
+ mTreeView->GetSelection(getter_AddRefs(selection));
+ if (selection) {
+ int32_t currentIndex = -1;
+ selection->GetCurrentIndex(&currentIndex);
+ if (currentIndex >= 0) return GetTreeItemAccessible(currentIndex);
+ }
+
+ return nullptr;
+}
+
+void XULTreeAccessible::SetCurrentItem(const LocalAccessible* 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++) {
+ LocalAccessible* 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: LocalAccessible implementation
+
+LocalAccessible* XULTreeAccessible::LocalChildAt(uint32_t aIndex) const {
+ uint32_t childCount = LocalAccessible::ChildCount();
+ if (aIndex < childCount) {
+ return LocalAccessible::LocalChildAt(aIndex);
+ }
+
+ return GetTreeItemAccessible(aIndex - childCount);
+}
+
+uint32_t XULTreeAccessible::ChildCount() const {
+ // Tree's children count is row count + treecols count.
+ uint32_t childCount = LocalAccessible::ChildCount();
+ if (!mTreeView) return childCount;
+
+ int32_t rowCount = 0;
+ mTreeView->GetRowCount(&rowCount);
+ childCount += rowCount;
+
+ return childCount;
+}
+
+Relation XULTreeAccessible::RelationByType(RelationType aType) const {
+ if (aType == RelationType::NODE_PARENT_OF) {
+ if (mTreeView) {
+ return Relation(new XULTreeItemIterator(this, mTreeView, -1));
+ }
+
+ return Relation();
+ }
+
+ return LocalAccessible::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;
+}
+
+LocalAccessible* XULTreeAccessible::ContainerWidget() const {
+ if (IsAutoCompletePopup() && mContent->GetParent()) {
+ // 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 =
+ mContent->GetParent()->AsElement()->AsXULMenuList();
+ if (menuListElm) {
+ RefPtr<mozilla::dom::Element> inputElm;
+ menuListElm->GetInputField(getter_AddRefs(inputElm));
+ if (inputElm) {
+ LocalAccessible* input = mDoc->GetAccessible(inputElm);
+ return input ? input->ContainerWidget() : nullptr;
+ }
+ }
+ }
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeAccessible: public implementation
+
+LocalAccessible* 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));
+ return mAccessibleCache.WithEntryHandle(
+ key, [&](auto&& entry) -> LocalAccessible* {
+ if (entry) {
+ return entry->get();
+ }
+
+ RefPtr<LocalAccessible> treeItem = CreateTreeItemAccessible(aRow);
+ if (treeItem) {
+ entry.Insert(RefPtr{treeItem});
+ Document()->BindToDocument(treeItem, nullptr);
+ return treeItem.get();
+ }
+
+ 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));
+ LocalAccessible* 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));
+ LocalAccessible* 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;
+ }
+
+ RefPtr<nsTreeColumns> treeColumns = mTree->GetColumns();
+ if (!treeColumns) return;
+
+ int32_t endCol = aEndCol;
+
+ if (endCol == -1) {
+ // We need to make sure to cast to int32_t before we do the subtraction, in
+ // case the column count is 0.
+ endCol = static_cast<int32_t>(treeColumns->Count()) - 1;
+ }
+
+ for (int32_t rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) {
+ void* key = reinterpret_cast<void*>(intptr_t(rowIdx));
+ LocalAccessible* 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;
+ LocalAccessible* item = CurrentItem();
+ if (item) {
+ FocusMgr()->ActiveItemChanged(item, true);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeAccessible: protected implementation
+
+already_AddRefed<LocalAccessible> XULTreeAccessible::CreateTreeItemAccessible(
+ int32_t aRow) const {
+ RefPtr<LocalAccessible> accessible = new XULTreeItemAccessible(
+ mContent, mDoc, const_cast<XULTreeAccessible*>(this), mTree, mTreeView,
+ aRow);
+
+ return accessible.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeItemAccessibleBase
+////////////////////////////////////////////////////////////////////////////////
+
+XULTreeItemAccessibleBase::XULTreeItemAccessibleBase(
+ nsIContent* aContent, DocAccessible* aDoc, LocalAccessible* aParent,
+ dom::XULTreeElement* 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, LocalAccessible,
+ mTree)
+
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase,
+ LocalAccessible,
+ XULTreeItemAccessibleBase)
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeItemAccessibleBase: LocalAccessible
+
+Accessible* XULTreeItemAccessibleBase::FocusedChild() {
+ return FocusMgr()->FocusedLocalAccessible() == this ? this : nullptr;
+}
+
+nsIntRect XULTreeItemAccessibleBase::BoundsInCSSPixels() const {
+ // Get x coordinate and width from treechildren element, get y coordinate and
+ // height from tree cell.
+
+ RefPtr<nsTreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
+
+ nsresult rv;
+ nsIntRect rect = mTree->GetCoordsForCellItem(mRow, column, u"cell"_ns, rv);
+ if (NS_FAILED(rv)) {
+ return nsIntRect();
+ }
+
+ RefPtr<dom::Element> bodyElement = mTree->GetTreeBody();
+ if (!bodyElement || !bodyElement->IsXULElement()) {
+ return nsIntRect();
+ }
+
+ rect.width = bodyElement->ClientWidth();
+
+ nsIFrame* bodyFrame = bodyElement->GetPrimaryFrame();
+ if (!bodyFrame) {
+ return nsIntRect();
+ }
+
+ CSSIntRect screenRect = bodyFrame->GetScreenRect();
+ rect.x = screenRect.x;
+ rect.y += screenRect.y;
+ return rect;
+}
+
+nsRect XULTreeItemAccessibleBase::BoundsInAppUnits() const {
+ nsIntRect bounds = BoundsInCSSPixels();
+ nsPresContext* presContext = mDoc->PresContext();
+ return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
+ presContext->CSSPixelsToAppUnits(bounds.Y()),
+ presContext->CSSPixelsToAppUnits(bounds.Width()),
+ presContext->CSSPixelsToAppUnits(bounds.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() const {
+ nsCOMPtr<nsITreeSelection> selection;
+ mTreeView->GetSelection(getter_AddRefs(selection));
+ if (selection) selection->SetCurrentIndex(mRow);
+
+ // focus event will be fired here
+ LocalAccessible::TakeFocus();
+}
+
+Relation XULTreeItemAccessibleBase::RelationByType(RelationType aType) const {
+ 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();
+ }
+}
+
+bool XULTreeItemAccessibleBase::HasPrimaryAction() const { return true; }
+
+uint8_t XULTreeItemAccessibleBase::ActionCount() const {
+ // "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) const {
+ if (aIndex != eAction_Click &&
+ (aIndex != eAction_Expand || !IsExpandable())) {
+ return false;
+ }
+
+ DoCommand(nullptr, aIndex);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeItemAccessibleBase: LocalAccessible implementation
+
+void XULTreeItemAccessibleBase::Shutdown() {
+ mTree = nullptr;
+ mTreeView = nullptr;
+ mRow = -1;
+ mParent = nullptr; // null-out to prevent base class's shutdown ops
+
+ 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() const {
+ // 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 = mTree->GetFirstVisibleRow();
+ int32_t lastVisibleRow = mTree->GetLastVisibleRow();
+ 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
+
+LocalAccessible* XULTreeItemAccessibleBase::ContainerWidget() const {
+ return mParent;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeItemAccessibleBase: LocalAccessible protected methods
+
+void XULTreeItemAccessibleBase::DispatchClickEvent(
+ nsIContent* aContent, uint32_t aActionIndex) const {
+ if (IsDefunct()) return;
+
+ RefPtr<nsTreeColumns> columns = mTree->GetColumns();
+ if (!columns) return;
+
+ // Get column and pseudo element.
+ RefPtr<nsTreeColumn> column;
+ nsAutoString pseudoElm;
+
+ if (aActionIndex == eAction_Click) {
+ // Key column is visible and clickable.
+ column = columns->GetKeyColumn();
+ } else {
+ // Primary column contains a twisty we should click on.
+ column = columns->GetPrimaryColumn();
+ pseudoElm = u"twisty"_ns;
+ }
+
+ if (column) {
+ RefPtr<dom::XULTreeElement> tree = mTree;
+ nsCoreUtils::DispatchClickEvent(tree, mRow, column, pseudoElm);
+ }
+}
+
+LocalAccessible* XULTreeItemAccessibleBase::GetSiblingAtOffset(
+ int32_t aOffset, nsresult* aError) const {
+ if (aError) *aError = NS_OK; // fail peacefully
+
+ return mParent->LocalChildAt(IndexInParent() + aOffset);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeItemAccessibleBase: protected implementation
+
+bool XULTreeItemAccessibleBase::IsExpandable() const {
+ bool isContainer = false;
+ mTreeView->IsContainer(mRow, &isContainer);
+ if (isContainer) {
+ bool isEmpty = false;
+ mTreeView->IsContainerEmpty(mRow, &isEmpty);
+ if (!isEmpty) {
+ RefPtr<nsTreeColumns> columns = mTree->GetColumns();
+ if (columns) {
+ nsTreeColumn* primaryColumn = columns->GetPrimaryColumn();
+ if (primaryColumn && !nsCoreUtils::IsColumnHidden(primaryColumn)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void XULTreeItemAccessibleBase::GetCellName(nsTreeColumn* aColumn,
+ nsAString& aName) const {
+ 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, LocalAccessible* aParent,
+ dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow)
+ : XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView,
+ aRow) {
+ mStateFlags |= eNoKidsFromDOM;
+ mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree, FlushType::None);
+ GetCellName(mColumn, mCachedName);
+}
+
+XULTreeItemAccessible::~XULTreeItemAccessible() {}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeItemAccessible: nsISupports implementation
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible,
+ XULTreeItemAccessibleBase, mColumn)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(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) const {
+ aName.Truncate();
+
+ GetCellName(mColumn, aName);
+ return eNameOK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeItemAccessible: LocalAccessible implementation
+
+void XULTreeItemAccessible::Shutdown() {
+ mColumn = nullptr;
+ XULTreeItemAccessibleBase::Shutdown();
+}
+
+role XULTreeItemAccessible::NativeRole() const {
+ RefPtr<nsTreeColumns> columns = mTree->GetColumns();
+ if (!columns) {
+ NS_ERROR("No tree columns object in the tree!");
+ return roles::NOTHING;
+ }
+
+ nsTreeColumn* primaryColumn = columns->GetPrimaryColumn();
+
+ 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) {}
+
+LocalAccessible* XULTreeColumAccessible::GetSiblingAtOffset(
+ int32_t aOffset, nsresult* aError) const {
+ if (aOffset < 0) {
+ return XULColumAccessible::GetSiblingAtOffset(aOffset, aError);
+ }
+
+ if (aError) *aError = NS_OK; // fail peacefully
+
+ RefPtr<dom::XULTreeElement> tree = nsCoreUtils::GetTree(mContent);
+ if (tree) {
+ nsCOMPtr<nsITreeView> treeView = tree->GetView(FlushType::None);
+ if (treeView) {
+ int32_t rowCount = 0;
+ treeView->GetRowCount(&rowCount);
+ if (rowCount > 0 && aOffset <= rowCount) {
+ XULTreeAccessible* treeAcc = LocalParent()->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 0000000000..6719969b4b
--- /dev/null
+++ b/accessible/xul/XULTreeAccessible.h
@@ -0,0 +1,276 @@
+/* -*- 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 "nsITreeView.h"
+#include "XULListboxAccessible.h"
+#include "mozilla/dom/XULTreeElement.h"
+
+class nsTreeBodyFrame;
+class nsTreeColumn;
+
+namespace mozilla {
+namespace a11y {
+
+class XULTreeGridCellAccessible;
+
+/*
+ * A class the represents the XUL Tree widget.
+ */
+const uint32_t kMaxTreeColumns = 100;
+const uint32_t kDefaultTreeCacheLength = 128;
+
+/**
+ * LocalAccessible 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, LocalAccessible)
+
+ // LocalAccessible
+ virtual void Shutdown() override;
+ virtual void Value(nsString& aValue) const override;
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+ virtual LocalAccessible* LocalChildAtPoint(
+ int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) override;
+
+ virtual LocalAccessible* LocalChildAt(uint32_t aIndex) const override;
+ virtual uint32_t ChildCount() const override;
+ virtual Relation RelationByType(RelationType aType) const 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 LocalAccessible* CurrentItem() const override;
+ virtual void SetCurrentItem(const LocalAccessible* aItem) override;
+
+ virtual LocalAccessible* 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
+ */
+ LocalAccessible* 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<LocalAccessible> CreateTreeItemAccessible(
+ int32_t aRow) const;
+
+ RefPtr<dom::XULTreeElement> 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,
+ LocalAccessible* aParent,
+ dom::XULTreeElement* aTree, nsITreeView* aTreeView,
+ int32_t aRow);
+
+ // nsISupports and cycle collection
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessibleBase,
+ AccessibleWrap)
+
+ // LocalAccessible
+ virtual void Shutdown() override;
+ virtual nsRect BoundsInAppUnits() const override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual nsIntRect BoundsInCSSPixels() const override;
+ virtual GroupPos GroupPosition() override;
+ virtual uint64_t NativeState() const override;
+ virtual uint64_t NativeInteractiveState() const override;
+ virtual int32_t IndexInParent() const override;
+ virtual Relation RelationByType(RelationType aType) const override;
+ virtual Accessible* FocusedChild() override;
+ virtual void SetSelected(bool aSelect) override;
+ virtual void TakeFocus() const override;
+
+ // ActionAccessible
+ virtual uint8_t ActionCount() const override;
+ virtual bool HasPrimaryAction() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
+ virtual bool DoAction(uint8_t aIndex) const override;
+
+ // Widgets
+ virtual LocalAccessible* 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(
+ nsTreeColumn* 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 };
+
+ // LocalAccessible
+ MOZ_CAN_RUN_SCRIPT
+ virtual void DispatchClickEvent(nsIContent* aContent,
+ uint32_t aActionIndex) const override;
+ virtual LocalAccessible* GetSiblingAtOffset(
+ int32_t aOffset, nsresult* aError = nullptr) const override;
+
+ // XULTreeItemAccessibleBase
+
+ /**
+ * Return true if the tree item accessible is expandable (contains subrows).
+ */
+ bool IsExpandable() const;
+
+ /**
+ * Return name for cell at the given column.
+ */
+ void GetCellName(nsTreeColumn* aColumn, nsAString& aName) const;
+
+ RefPtr<dom::XULTreeElement> mTree;
+ nsITreeView* mTreeView;
+ int32_t mRow;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(XULTreeItemAccessibleBase,
+ XULTREEITEMBASEACCESSIBLE_IMPL_CID)
+
+/**
+ * LocalAccessible class for items for XUL tree.
+ */
+class XULTreeItemAccessible : public XULTreeItemAccessibleBase {
+ public:
+ XULTreeItemAccessible(nsIContent* aContent, DocAccessible* aDoc,
+ LocalAccessible* aParent, dom::XULTreeElement* aTree,
+ nsITreeView* aTreeView, int32_t aRow);
+
+ // nsISupports and cycle collection
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessible,
+ XULTreeItemAccessibleBase)
+
+ // LocalAccessible
+ virtual void Shutdown() override;
+ virtual ENameValueFlag Name(nsString& aName) const override;
+ virtual a11y::role NativeRole() const override;
+
+ // XULTreeItemAccessibleBase
+ virtual void RowInvalidated(int32_t aStartColIdx,
+ int32_t aEndColIdx) override;
+
+ protected:
+ virtual ~XULTreeItemAccessible();
+
+ // XULTreeItemAccessible
+ RefPtr<nsTreeColumn> mColumn;
+ nsString mCachedName;
+};
+
+/**
+ * LocalAccessible class for columns element of XUL tree.
+ */
+class XULTreeColumAccessible : public XULColumAccessible {
+ public:
+ XULTreeColumAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ protected:
+ // LocalAccessible
+ virtual LocalAccessible* GetSiblingAtOffset(
+ int32_t aOffset, nsresult* aError = nullptr) const override;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// LocalAccessible downcasting method
+
+inline XULTreeAccessible* LocalAccessible::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 0000000000..783f98e7c3
--- /dev/null
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -0,0 +1,653 @@
+/* -*- 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 "XULTreeGridAccessible.h"
+
+#include "AccAttributes.h"
+#include "LocalAccessible-inl.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 "nsTreeColumns.h"
+
+#include "nsITreeSelection.h"
+#include "nsComponentManagerUtils.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TreeColumnBinding.h"
+#include "mozilla/dom/XULTreeElementBinding.h"
+
+using namespace mozilla::a11y;
+using namespace mozilla;
+
+XULTreeGridAccessible::~XULTreeGridAccessible() {}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeGridAccessible: Table
+
+uint32_t XULTreeGridAccessible::ColCount() const {
+ 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++) {
+ LocalAccessible* 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);
+ }
+}
+
+LocalAccessible* XULTreeGridAccessible::CellAt(uint32_t aRowIndex,
+ uint32_t aColumnIndex) {
+ LocalAccessible* row = GetTreeItemAccessible(aRowIndex);
+ if (!row) return nullptr;
+
+ RefPtr<nsTreeColumn> 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();
+
+ LocalAccessible* treeColumns = LocalAccessible::LocalChildAt(0);
+ if (treeColumns) {
+ LocalAccessible* treeColumnItem = treeColumns->LocalChildAt(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: LocalAccessible implementation
+
+role XULTreeGridAccessible::NativeRole() const {
+ RefPtr<nsTreeColumns> treeColumns = mTree->GetColumns(FlushType::None);
+ if (!treeColumns) {
+ NS_ERROR("No treecolumns object for tree!");
+ return roles::NOTHING;
+ }
+
+ nsTreeColumn* primaryColumn = treeColumns->GetPrimaryColumn();
+
+ return primaryColumn ? roles::TREE_TABLE : roles::TABLE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeGridAccessible: XULTreeAccessible implementation
+
+already_AddRefed<LocalAccessible>
+XULTreeGridAccessible::CreateTreeItemAccessible(int32_t aRow) const {
+ RefPtr<LocalAccessible> accessible = new XULTreeGridRowAccessible(
+ mContent, mDoc, const_cast<XULTreeGridAccessible*>(this), mTree,
+ mTreeView, aRow);
+
+ return accessible.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeGridRowAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULTreeGridRowAccessible::XULTreeGridRowAccessible(
+ nsIContent* aContent, DocAccessible* aDoc, LocalAccessible* aTreeAcc,
+ dom::XULTreeElement* 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(XULTreeGridRowAccessible)
+NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase)
+
+NS_IMPL_ADDREF_INHERITED(XULTreeGridRowAccessible, XULTreeItemAccessibleBase)
+NS_IMPL_RELEASE_INHERITED(XULTreeGridRowAccessible, XULTreeItemAccessibleBase)
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeGridRowAccessible: LocalAccessible implementation
+
+void XULTreeGridRowAccessible::Shutdown() {
+ if (mDoc && !mDoc->IsDefunct()) {
+ UnbindCacheEntriesFromDocument(mAccessibleCache);
+ }
+
+ XULTreeItemAccessibleBase::Shutdown();
+}
+
+role XULTreeGridRowAccessible::NativeRole() const { return roles::ROW; }
+
+ENameValueFlag XULTreeGridRowAccessible::Name(nsString& aName) const {
+ aName.Truncate();
+
+ // XXX: the row name sholdn't be a concatenation of cell names (bug 664384).
+ RefPtr<nsTreeColumn> 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;
+}
+
+LocalAccessible* XULTreeGridRowAccessible::LocalChildAtPoint(
+ int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) {
+ nsIFrame* frame = GetFrame();
+ if (!frame) return nullptr;
+
+ nsPresContext* presContext = frame->PresContext();
+ PresShell* presShell = presContext->PresShell();
+
+ nsIFrame* rootFrame = presShell->GetRootFrame();
+ NS_ENSURE_TRUE(rootFrame, nullptr);
+
+ CSSIntRect rootRect = rootFrame->GetScreenRect();
+
+ int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.X();
+ int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.Y();
+
+ ErrorResult rv;
+ dom::TreeCellInfo cellInfo;
+ mTree->GetCellAt(clientX, clientY, cellInfo, rv);
+
+ // Return if we failed to find tree cell in the row for the given point.
+ if (cellInfo.mRow != mRow || !cellInfo.mCol) return nullptr;
+
+ return GetCellAccessible(cellInfo.mCol);
+}
+
+LocalAccessible* XULTreeGridRowAccessible::LocalChildAt(uint32_t aIndex) const {
+ if (IsDefunct()) return nullptr;
+
+ RefPtr<nsTreeColumn> 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(
+ nsTreeColumn* aColumn) const {
+ MOZ_ASSERT(aColumn, "No tree column!");
+
+ void* key = static_cast<void*>(aColumn);
+ XULTreeGridCellAccessible* cachedCell = mAccessibleCache.GetWeak(key);
+ if (cachedCell) return cachedCell;
+
+ RefPtr<XULTreeGridCellAccessible> cell = new XULTreeGridCellAccessible(
+ mContent, mDoc, const_cast<XULTreeGridRowAccessible*>(this), mTree,
+ mTreeView, mRow, aColumn);
+ mAccessibleCache.InsertOrUpdate(key, RefPtr{cell});
+ Document()->BindToDocument(cell, nullptr);
+ return cell;
+}
+
+void XULTreeGridRowAccessible::RowInvalidated(int32_t aStartColIdx,
+ int32_t aEndColIdx) {
+ RefPtr<nsTreeColumns> treeColumns = mTree->GetColumns(FlushType::None);
+ if (!treeColumns) return;
+
+ bool nameChanged = false;
+ for (int32_t colIdx = aStartColIdx; colIdx <= aEndColIdx; ++colIdx) {
+ nsTreeColumn* column = treeColumns->GetColumnAt(colIdx);
+ 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, dom::XULTreeElement* aTree,
+ nsITreeView* aTreeView, int32_t aRow, nsTreeColumn* aColumn)
+ : LeafAccessible(aContent, aDoc),
+ mTree(aTree),
+ mTreeView(aTreeView),
+ mRow(aRow),
+ mColumn(aColumn) {
+ mParent = aRowAcc;
+ mStateFlags |= eSharedNode;
+ mGenericTypes |= eTableCell;
+
+ NS_ASSERTION(mTreeView, "mTreeView is null");
+
+ if (mColumn->Type() == dom::TreeColumn_Binding::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(XULTreeGridCellAccessible)
+NS_INTERFACE_MAP_END_INHERITING(LeafAccessible)
+NS_IMPL_ADDREF_INHERITED(XULTreeGridCellAccessible, LeafAccessible)
+NS_IMPL_RELEASE_INHERITED(XULTreeGridCellAccessible, LeafAccessible)
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeGridCellAccessible: LocalAccessible
+
+void XULTreeGridCellAccessible::Shutdown() {
+ mTree = nullptr;
+ mTreeView = nullptr;
+ mRow = -1;
+ mColumn = nullptr;
+ mParent = nullptr; // null-out to prevent base class's shutdown ops
+
+ LeafAccessible::Shutdown();
+}
+
+Accessible* XULTreeGridCellAccessible::FocusedChild() { return nullptr; }
+
+ENameValueFlag XULTreeGridCellAccessible::Name(nsString& aName) const {
+ 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::BoundsInCSSPixels() const {
+ // Get bounds for tree cell and add x and y of treechildren element to
+ // x and y of the cell.
+ nsresult rv;
+ nsIntRect rect = mTree->GetCoordsForCellItem(mRow, mColumn, u"cell"_ns, rv);
+ if (NS_FAILED(rv)) {
+ return nsIntRect();
+ }
+
+ RefPtr<dom::Element> bodyElement = mTree->GetTreeBody();
+ if (!bodyElement || !bodyElement->IsXULElement()) {
+ return nsIntRect();
+ }
+
+ nsIFrame* bodyFrame = bodyElement->GetPrimaryFrame();
+ if (!bodyFrame) {
+ return nsIntRect();
+ }
+
+ CSSIntRect screenRect = bodyFrame->GetScreenRect();
+ rect.x += screenRect.x;
+ rect.y += screenRect.y;
+ return rect;
+}
+
+nsRect XULTreeGridCellAccessible::BoundsInAppUnits() const {
+ nsIntRect bounds = BoundsInCSSPixels();
+ nsPresContext* presContext = mDoc->PresContext();
+ return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
+ presContext->CSSPixelsToAppUnits(bounds.Y()),
+ presContext->CSSPixelsToAppUnits(bounds.Width()),
+ presContext->CSSPixelsToAppUnits(bounds.Height()));
+}
+
+bool XULTreeGridCellAccessible::HasPrimaryAction() const {
+ return mColumn->Cycler() ||
+ (mColumn->Type() == dom::TreeColumn_Binding::TYPE_CHECKBOX &&
+ IsEditable());
+}
+
+void XULTreeGridCellAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
+ aName.Truncate();
+
+ if (aIndex != eAction_Click || !mTreeView) return;
+
+ if (mColumn->Cycler()) {
+ aName.AssignLiteral("cycle");
+ return;
+ }
+
+ if (mColumn->Type() == dom::TreeColumn_Binding::TYPE_CHECKBOX &&
+ IsEditable()) {
+ nsAutoString value;
+ mTreeView->GetCellValue(mRow, mColumn, value);
+ if (value.EqualsLiteral("true")) {
+ aName.AssignLiteral("uncheck");
+ } else {
+ aName.AssignLiteral("check");
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeGridCellAccessible: TableCell
+
+TableAccessible* XULTreeGridCellAccessible::Table() const {
+ LocalAccessible* grandParent = mParent->LocalParent();
+ if (grandParent) return grandParent->AsTable();
+
+ return nullptr;
+}
+
+uint32_t XULTreeGridCellAccessible::ColIdx() const {
+ uint32_t colIdx = 0;
+ RefPtr<nsTreeColumn> column = mColumn;
+ while ((column = nsCoreUtils::GetPreviousSensibleColumn(column))) colIdx++;
+
+ return colIdx;
+}
+
+uint32_t XULTreeGridCellAccessible::RowIdx() const { return mRow; }
+
+void XULTreeGridCellAccessible::ColHeaderCells(
+ nsTArray<Accessible*>* aHeaderCells) {
+ dom::Element* columnElm = mColumn->Element();
+
+ LocalAccessible* headerCell = mDoc->GetAccessible(columnElm);
+ 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: LocalAccessible public implementation
+
+already_AddRefed<AccAttributes> XULTreeGridCellAccessible::NativeAttributes() {
+ RefPtr<AccAttributes> attributes = new AccAttributes();
+
+ // "table-cell-index" attribute
+ TableAccessible* table = Table();
+ if (!table) return attributes.forget();
+
+ attributes->SetAttribute(nsGkAtoms::tableCellIndex,
+ table->CellIndexAt(mRow, ColIdx()));
+
+ // "cycles" attribute
+ if (mColumn->Cycler()) {
+ attributes->SetAttribute(nsGkAtoms::cycles, true);
+ }
+
+ return attributes.forget();
+}
+
+role XULTreeGridCellAccessible::NativeRole() const { return roles::GRID_CELL; }
+
+uint64_t XULTreeGridCellAccessible::NativeState() const {
+ 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
+ if (mColumn->Type() == dom::TreeColumn_Binding::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) const {
+ return Relation();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULTreeGridCellAccessible: public implementation
+
+bool XULTreeGridCellAccessible::CellInvalidated() {
+ nsAutoString textEquiv;
+
+ if (mColumn->Type() == dom::TreeColumn_Binding::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: LocalAccessible protected implementation
+
+LocalAccessible* XULTreeGridCellAccessible::GetSiblingAtOffset(
+ int32_t aOffset, nsresult* aError) const {
+ if (aError) *aError = NS_OK; // fail peacefully
+
+ RefPtr<nsTreeColumn> 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(LocalParent());
+ return rowAcc->GetCellAccessible(columnAtOffset);
+}
+
+void XULTreeGridCellAccessible::DispatchClickEvent(
+ nsIContent* aContent, uint32_t aActionIndex) const {
+ if (IsDefunct()) return;
+
+ RefPtr<dom::XULTreeElement> tree = mTree;
+ RefPtr<nsTreeColumn> column = mColumn;
+ nsCoreUtils::DispatchClickEvent(tree, mRow, column);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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;
+
+ dom::Element* columnElm = mColumn->Element();
+
+ if (!columnElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
+ nsGkAtoms::_true, eCaseMatters)) {
+ return false;
+ }
+
+ return mContent->AsElement()->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 0000000000..d01592148e
--- /dev/null
+++ b/accessible/xul/XULTreeGridAccessible.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- 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"
+
+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() const override;
+ virtual uint32_t RowCount() override;
+ virtual LocalAccessible* 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 LocalAccessible* AsAccessible() override { return this; }
+
+ // LocalAccessible
+ virtual TableAccessible* AsTable() override { return this; }
+ virtual a11y::role NativeRole() const override;
+
+ protected:
+ virtual ~XULTreeGridAccessible();
+
+ // XULTreeAccessible
+ virtual already_AddRefed<LocalAccessible> 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 LocalAccessible::LocalChildAt;
+
+ XULTreeGridRowAccessible(nsIContent* aContent, DocAccessible* aDoc,
+ LocalAccessible* aParent, dom::XULTreeElement* aTree,
+ nsITreeView* aTreeView, int32_t aRow);
+
+ // nsISupports and cycle collection
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeGridRowAccessible,
+ XULTreeItemAccessibleBase)
+
+ // LocalAccessible
+ virtual void Shutdown() override;
+ virtual a11y::role NativeRole() const override;
+ virtual ENameValueFlag Name(nsString& aName) const override;
+ virtual LocalAccessible* LocalChildAtPoint(
+ int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) override;
+
+ virtual LocalAccessible* LocalChildAt(uint32_t aIndex) const override;
+ virtual uint32_t ChildCount() const override;
+
+ // XULTreeItemAccessibleBase
+ XULTreeGridCellAccessible* GetCellAccessible(
+ nsTreeColumn* aColumn) const 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,
+ dom::XULTreeElement* aTree, nsITreeView* aTreeView,
+ int32_t aRow, nsTreeColumn* aColumn);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeGridCellAccessible,
+ LeafAccessible)
+
+ // LocalAccessible
+ virtual void Shutdown() override;
+ virtual TableCellAccessible* AsTableCell() override { return this; }
+ virtual nsRect BoundsInAppUnits() const override;
+ virtual nsIntRect BoundsInCSSPixels() const override;
+ virtual ENameValueFlag Name(nsString& aName) const override;
+ virtual Accessible* FocusedChild() override;
+ virtual already_AddRefed<AccAttributes> NativeAttributes() override;
+ virtual int32_t IndexInParent() const override;
+ virtual Relation RelationByType(RelationType aType) const override;
+ virtual a11y::role NativeRole() const override;
+ virtual uint64_t NativeState() const override;
+ virtual uint64_t NativeInteractiveState() const override;
+
+ // ActionAccessible
+ virtual bool HasPrimaryAction() const override;
+ virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) 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();
+
+ // LocalAccessible
+ virtual LocalAccessible* GetSiblingAtOffset(
+ int32_t aOffset, nsresult* aError = nullptr) const override;
+ MOZ_CAN_RUN_SCRIPT
+ virtual void DispatchClickEvent(nsIContent* aContent,
+ uint32_t aActionIndex) const override;
+
+ // XULTreeGridCellAccessible
+
+ /**
+ * Return true if value of cell can be modified.
+ */
+ bool IsEditable() const;
+
+ enum { eAction_Click = 0 };
+
+ RefPtr<dom::XULTreeElement> mTree;
+ nsITreeView* mTreeView;
+
+ int32_t mRow;
+ RefPtr<nsTreeColumn> 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 0000000000..4fccfff6e0
--- /dev/null
+++ b/accessible/xul/moz.build
@@ -0,0 +1,56 @@
+# -*- 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",
+ "XULComboboxAccessible.cpp",
+ "XULElementAccessibles.cpp",
+ "XULFormControlAccessible.cpp",
+ "XULListboxAccessible.cpp",
+ "XULMenuAccessible.cpp",
+ "XULSelectControlAccessible.cpp",
+ "XULTabAccessible.cpp",
+ "XULTreeAccessible.cpp",
+ "XULTreeGridAccessible.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/accessible/base",
+ "/accessible/generic",
+ "/accessible/html",
+ "/accessible/xpcom",
+ "/dom/base",
+ "/dom/xul",
+ "/layout/generic",
+ "/layout/xul",
+ "/layout/xul/tree",
+]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ 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",
+ ]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+ LOCAL_INCLUDES += [
+ "/accessible/android",
+ ]
+else:
+ LOCAL_INCLUDES += [
+ "/accessible/other",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"