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