summaryrefslogtreecommitdiff
path: root/accessible/html
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/html')
-rw-r--r--accessible/html/HTMLCanvasAccessible.cpp24
-rw-r--r--accessible/html/HTMLCanvasAccessible.h35
-rw-r--r--accessible/html/HTMLElementAccessibles.cpp204
-rw-r--r--accessible/html/HTMLElementAccessibles.h120
-rw-r--r--accessible/html/HTMLFormControlAccessible.cpp841
-rw-r--r--accessible/html/HTMLFormControlAccessible.h284
-rw-r--r--accessible/html/HTMLImageMapAccessible.cpp228
-rw-r--r--accessible/html/HTMLImageMapAccessible.h89
-rw-r--r--accessible/html/HTMLLinkAccessible.cpp147
-rw-r--r--accessible/html/HTMLLinkAccessible.h51
-rw-r--r--accessible/html/HTMLListAccessible.cpp199
-rw-r--r--accessible/html/HTMLListAccessible.h105
-rw-r--r--accessible/html/HTMLSelectAccessible.cpp610
-rw-r--r--accessible/html/HTMLSelectAccessible.h222
-rw-r--r--accessible/html/HTMLTableAccessible.cpp1139
-rw-r--r--accessible/html/HTMLTableAccessible.h226
-rw-r--r--accessible/html/moz.build50
17 files changed, 4574 insertions, 0 deletions
diff --git a/accessible/html/HTMLCanvasAccessible.cpp b/accessible/html/HTMLCanvasAccessible.cpp
new file mode 100644
index 0000000000..4cc7487282
--- /dev/null
+++ b/accessible/html/HTMLCanvasAccessible.cpp
@@ -0,0 +1,24 @@
+/* -*- 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 "HTMLCanvasAccessible.h"
+
+#include "Role.h"
+
+using namespace mozilla::a11y;
+
+HTMLCanvasAccessible::
+ HTMLCanvasAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLCanvasAccessible, HyperTextAccessible)
+
+role
+HTMLCanvasAccessible::NativeRole()
+{
+ return roles::CANVAS;
+}
diff --git a/accessible/html/HTMLCanvasAccessible.h b/accessible/html/HTMLCanvasAccessible.h
new file mode 100644
index 0000000000..f07b84435d
--- /dev/null
+++ b/accessible/html/HTMLCanvasAccessible.h
@@ -0,0 +1,35 @@
+/* -*- 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_HTMLCanvasAccessible_h__
+#define mozilla_a11y_HTMLCanvasAccessible_h__
+
+#include "HyperTextAccessibleWrap.h"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * HTML canvas accessible (html:canvas).
+ */
+class HTMLCanvasAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLCanvasAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+
+protected:
+ virtual ~HTMLCanvasAccessible() { }
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/html/HTMLElementAccessibles.cpp b/accessible/html/HTMLElementAccessibles.cpp
new file mode 100644
index 0000000000..39601151b8
--- /dev/null
+++ b/accessible/html/HTMLElementAccessibles.cpp
@@ -0,0 +1,204 @@
+/* -*- 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 "HTMLElementAccessibles.h"
+
+#include "DocAccessible.h"
+#include "nsAccUtils.h"
+#include "nsIPersistentProperties2.h"
+#include "nsTextEquivUtils.h"
+#include "Relation.h"
+#include "Role.h"
+#include "States.h"
+
+#include "mozilla/dom/HTMLLabelElement.h"
+#include "mozilla/dom/HTMLDetailsElement.h"
+#include "mozilla/dom/HTMLSummaryElement.h"
+
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLHRAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+role
+HTMLHRAccessible::NativeRole()
+{
+ return roles::SEPARATOR;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLBRAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+role
+HTMLBRAccessible::NativeRole()
+{
+ return roles::WHITESPACE;
+}
+
+uint64_t
+HTMLBRAccessible::NativeState()
+{
+ return states::READONLY;
+}
+
+ENameValueFlag
+HTMLBRAccessible::NativeName(nsString& aName)
+{
+ aName = static_cast<char16_t>('\n'); // Newline char
+ return eNameOK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLLabelAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLLabelAccessible, HyperTextAccessible)
+
+ENameValueFlag
+HTMLLabelAccessible::NativeName(nsString& aName)
+{
+ nsTextEquivUtils::GetNameFromSubtree(this, aName);
+ return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
+}
+
+Relation
+HTMLLabelAccessible::RelationByType(RelationType aType)
+{
+ Relation rel = AccessibleWrap::RelationByType(aType);
+ if (aType == RelationType::LABEL_FOR) {
+ dom::HTMLLabelElement* label = dom::HTMLLabelElement::FromContent(mContent);
+ rel.AppendTarget(mDoc, label->GetControl());
+ }
+
+ return rel;
+}
+
+uint8_t
+HTMLLabelAccessible::ActionCount()
+{
+ return nsCoreUtils::IsLabelWithControl(mContent) ? 1 : 0;
+}
+
+void
+HTMLLabelAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ if (aIndex == 0) {
+ if (nsCoreUtils::IsLabelWithControl(mContent))
+ aName.AssignLiteral("click");
+ }
+}
+
+bool
+HTMLLabelAccessible::DoAction(uint8_t aIndex)
+{
+ if (aIndex != 0)
+ return false;
+
+ DoCommand();
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLOuputAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLOutputAccessible, HyperTextAccessible)
+
+Relation
+HTMLOutputAccessible::RelationByType(RelationType aType)
+{
+ Relation rel = AccessibleWrap::RelationByType(aType);
+ if (aType == RelationType::CONTROLLED_BY)
+ rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::_for));
+
+ return rel;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSummaryAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLSummaryAccessible::
+ HTMLSummaryAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+ mGenericTypes |= eButton;
+}
+
+uint8_t
+HTMLSummaryAccessible::ActionCount()
+{
+ return 1;
+}
+
+void
+HTMLSummaryAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ if (aIndex != eAction_Click) {
+ return;
+ }
+
+ dom::HTMLSummaryElement* summary = dom::HTMLSummaryElement::FromContent(mContent);
+ if (!summary) {
+ return;
+ }
+
+ dom::HTMLDetailsElement* details = summary->GetDetails();
+ if (!details) {
+ return;
+ }
+
+ if (details->Open()) {
+ aName.AssignLiteral("collapse");
+ } else {
+ aName.AssignLiteral("expand");
+ }
+}
+
+bool
+HTMLSummaryAccessible::DoAction(uint8_t aIndex)
+{
+ if (aIndex != eAction_Click)
+ return false;
+
+ DoCommand();
+ return true;
+}
+
+uint64_t
+HTMLSummaryAccessible::NativeState()
+{
+ uint64_t state = HyperTextAccessibleWrap::NativeState();
+
+ dom::HTMLSummaryElement* summary = dom::HTMLSummaryElement::FromContent(mContent);
+ if (!summary) {
+ return state;
+ }
+
+ dom::HTMLDetailsElement* details = summary->GetDetails();
+ if (!details) {
+ return state;
+ }
+
+ if (details->Open()) {
+ state |= states::EXPANDED;
+ } else {
+ state |= states::COLLAPSED;
+ }
+
+ return state;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSummaryAccessible: Widgets
+
+bool
+HTMLSummaryAccessible::IsWidget() const
+{
+ return true;
+}
diff --git a/accessible/html/HTMLElementAccessibles.h b/accessible/html/HTMLElementAccessibles.h
new file mode 100644
index 0000000000..83abbe9e62
--- /dev/null
+++ b/accessible/html/HTMLElementAccessibles.h
@@ -0,0 +1,120 @@
+/* -*- 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_HTMLElementAccessibles_h__
+#define mozilla_a11y_HTMLElementAccessibles_h__
+
+#include "BaseAccessibles.h"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Used for HTML hr element.
+ */
+class HTMLHRAccessible : public LeafAccessible
+{
+public:
+
+ HTMLHRAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ LeafAccessible(aContent, aDoc) {}
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+};
+
+/**
+ * Used for HTML br element.
+ */
+class HTMLBRAccessible : public LeafAccessible
+{
+public:
+ HTMLBRAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ LeafAccessible(aContent, aDoc)
+ {
+ mType = eHTMLBRType;
+ mGenericTypes |= eText;
+ }
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+
+protected:
+ // Accessible
+ virtual ENameValueFlag NativeName(nsString& aName) override;
+};
+
+/**
+ * Used for HTML label element.
+ */
+class HTMLLabelAccessible : public HyperTextAccessibleWrap
+{
+public:
+
+ HTMLLabelAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc) {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ 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;
+
+protected:
+ virtual ~HTMLLabelAccessible() {}
+ virtual ENameValueFlag NativeName(nsString& aName) override;
+};
+
+/**
+ * Used for HTML output element.
+ */
+class HTMLOutputAccessible : public HyperTextAccessibleWrap
+{
+public:
+
+ HTMLOutputAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc) {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ virtual Relation RelationByType(RelationType aType) override;
+
+protected:
+ virtual ~HTMLOutputAccessible() {}
+};
+
+/**
+ * Accessible for the HTML summary element.
+ */
+class HTMLSummaryAccessible : public HyperTextAccessibleWrap
+{
+
+public:
+ enum { eAction_Click = 0 };
+
+ HTMLSummaryAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // Accessible
+ 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;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/html/HTMLFormControlAccessible.cpp b/accessible/html/HTMLFormControlAccessible.cpp
new file mode 100644
index 0000000000..a7b3874a1b
--- /dev/null
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -0,0 +1,841 @@
+/* -*- 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 "HTMLFormControlAccessible.h"
+
+#include "Accessible-inl.h"
+#include "nsAccUtils.h"
+#include "nsEventShell.h"
+#include "nsTextEquivUtils.h"
+#include "Relation.h"
+#include "Role.h"
+#include "States.h"
+
+#include "nsContentList.h"
+#include "mozilla/dom/HTMLInputElement.h"
+#include "nsIDOMNSEditableElement.h"
+#include "nsIDOMHTMLTextAreaElement.h"
+#include "nsIEditor.h"
+#include "nsIFormControl.h"
+#include "nsIPersistentProperties2.h"
+#include "nsISelectionController.h"
+#include "nsIServiceManager.h"
+#include "nsITextControlFrame.h"
+#include "nsNameSpaceManager.h"
+#include "mozilla/dom/ScriptSettings.h"
+
+#include "mozilla/EventStates.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/Preferences.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLCheckboxAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+role
+HTMLCheckboxAccessible::NativeRole()
+{
+ return roles::CHECKBUTTON;
+}
+
+uint8_t
+HTMLCheckboxAccessible::ActionCount()
+{
+ return 1;
+}
+
+void
+HTMLCheckboxAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ if (aIndex == eAction_Click) { // 0 is the magic value for default action
+ uint64_t state = NativeState();
+ if (state & states::CHECKED)
+ aName.AssignLiteral("uncheck");
+ else if (state & states::MIXED)
+ aName.AssignLiteral("cycle");
+ else
+ aName.AssignLiteral("check");
+ }
+}
+
+bool
+HTMLCheckboxAccessible::DoAction(uint8_t aIndex)
+{
+ if (aIndex != 0)
+ return false;
+
+ DoCommand();
+ return true;
+}
+
+uint64_t
+HTMLCheckboxAccessible::NativeState()
+{
+ uint64_t state = LeafAccessible::NativeState();
+
+ state |= states::CHECKABLE;
+ HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
+ if (!input)
+ return state;
+
+ if (input->Indeterminate())
+ return state | states::MIXED;
+
+ if (input->Checked())
+ return state | states::CHECKED;
+
+ return state;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLCheckboxAccessible: Widgets
+
+bool
+HTMLCheckboxAccessible::IsWidget() const
+{
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLRadioButtonAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+uint64_t
+HTMLRadioButtonAccessible::NativeState()
+{
+ uint64_t state = AccessibleWrap::NativeState();
+
+ state |= states::CHECKABLE;
+
+ HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
+ if (input && input->Checked())
+ state |= states::CHECKED;
+
+ return state;
+}
+
+void
+HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
+ int32_t* aSetSize)
+{
+ int32_t namespaceId = mContent->NodeInfo()->NamespaceID();
+ nsAutoString tagName;
+ mContent->NodeInfo()->GetName(tagName);
+
+ nsAutoString type;
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
+ nsAutoString name;
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
+
+ RefPtr<nsContentList> inputElms;
+
+ nsCOMPtr<nsIFormControl> formControlNode(do_QueryInterface(mContent));
+ dom::Element* formElm = formControlNode->GetFormElement();
+ if (formElm)
+ inputElms = NS_GetContentList(formElm, namespaceId, tagName);
+ else
+ inputElms = NS_GetContentList(mContent->OwnerDoc(), namespaceId, tagName);
+ NS_ENSURE_TRUE_VOID(inputElms);
+
+ uint32_t inputCount = inputElms->Length(false);
+
+ // Compute posinset and setsize.
+ int32_t indexOf = 0;
+ int32_t count = 0;
+
+ for (uint32_t index = 0; index < inputCount; index++) {
+ nsIContent* inputElm = inputElms->Item(index, false);
+ if (inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ type, eCaseMatters) &&
+ inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
+ name, eCaseMatters) && mDoc->HasAccessible(inputElm)) {
+ count++;
+ if (inputElm == mContent)
+ indexOf = count;
+ }
+ }
+
+ *aPosInSet = indexOf;
+ *aSetSize = count;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLButtonAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLButtonAccessible::
+ HTMLButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+ mGenericTypes |= eButton;
+}
+
+uint8_t
+HTMLButtonAccessible::ActionCount()
+{
+ return 1;
+}
+
+void
+HTMLButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ if (aIndex == eAction_Click)
+ aName.AssignLiteral("press");
+}
+
+bool
+HTMLButtonAccessible::DoAction(uint8_t aIndex)
+{
+ if (aIndex != eAction_Click)
+ return false;
+
+ DoCommand();
+ return true;
+}
+
+uint64_t
+HTMLButtonAccessible::State()
+{
+ uint64_t state = HyperTextAccessibleWrap::State();
+ if (state == states::DEFUNCT)
+ return state;
+
+ // Inherit states from input@type="file" suitable for the button. Note,
+ // no special processing for unavailable state since inheritance is supplied
+ // other code paths.
+ if (mParent && mParent->IsHTMLFileInput()) {
+ uint64_t parentState = mParent->State();
+ state |= parentState & (states::BUSY | states::REQUIRED |
+ states::HASPOPUP | states::INVALID);
+ }
+
+ return state;
+}
+
+uint64_t
+HTMLButtonAccessible::NativeState()
+{
+ uint64_t state = HyperTextAccessibleWrap::NativeState();
+
+ EventStates elmState = mContent->AsElement()->State();
+ if (elmState.HasState(NS_EVENT_STATE_DEFAULT))
+ state |= states::DEFAULT;
+
+ return state;
+}
+
+role
+HTMLButtonAccessible::NativeRole()
+{
+ return roles::PUSHBUTTON;
+}
+
+ENameValueFlag
+HTMLButtonAccessible::NativeName(nsString& aName)
+{
+ // No need to check @value attribute for buttons since this attribute results
+ // in native anonymous text node and the name is calculated from subtree.
+ // The same magic works for @alt and @value attributes in case of type="image"
+ // element that has no valid @src (note if input@type="image" has an image
+ // then neither @alt nor @value attributes are used to generate a visual label
+ // and thus we need to obtain the accessible name directly from attribute
+ // value). Also the same algorithm works in case of default labels for
+ // type="submit"/"reset"/"image" elements.
+
+ ENameValueFlag nameFlag = Accessible::NativeName(aName);
+ if (!aName.IsEmpty() || !mContent->IsHTMLElement(nsGkAtoms::input) ||
+ !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::image, eCaseMatters))
+ return nameFlag;
+
+ if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName))
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
+
+ aName.CompressWhitespace();
+ return eNameOK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLButtonAccessible: Widgets
+
+bool
+HTMLButtonAccessible::IsWidget() const
+{
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTextFieldAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLTextFieldAccessible::
+ HTMLTextFieldAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+ mType = eHTMLTextFieldType;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLTextFieldAccessible,
+ HyperTextAccessible)
+
+role
+HTMLTextFieldAccessible::NativeRole()
+{
+ if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::password, eIgnoreCase)) {
+ return roles::PASSWORD_TEXT;
+ }
+
+ return roles::ENTRY;
+}
+
+already_AddRefed<nsIPersistentProperties>
+HTMLTextFieldAccessible::NativeAttributes()
+{
+ nsCOMPtr<nsIPersistentProperties> attributes =
+ HyperTextAccessibleWrap::NativeAttributes();
+
+ // Expose type for text input elements as it gives some useful context,
+ // especially for mobile.
+ nsAutoString type;
+ if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
+ nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType, type);
+ if (!ARIARoleMap() && type.EqualsLiteral("search")) {
+ nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
+ NS_LITERAL_STRING("searchbox"));
+ }
+ }
+
+ return attributes.forget();
+}
+
+ENameValueFlag
+HTMLTextFieldAccessible::NativeName(nsString& aName)
+{
+ ENameValueFlag nameFlag = Accessible::NativeName(aName);
+ if (!aName.IsEmpty())
+ return nameFlag;
+
+ // If part of compound of XUL widget then grab a name from XUL widget element.
+ nsIContent* widgetElm = XULWidgetElm();
+ if (widgetElm)
+ XULElmName(mDoc, widgetElm, aName);
+
+ if (!aName.IsEmpty())
+ return eNameOK;
+
+ // text inputs and textareas might have useful placeholder text
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, aName);
+ return eNameOK;
+}
+
+void
+HTMLTextFieldAccessible::Value(nsString& aValue)
+{
+ aValue.Truncate();
+ if (NativeState() & states::PROTECTED) // Don't return password text!
+ return;
+
+ nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea(do_QueryInterface(mContent));
+ if (textArea) {
+ textArea->GetValue(aValue);
+ return;
+ }
+
+ HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
+ if (input)
+ input->GetValue(aValue);
+}
+
+void
+HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState) const
+{
+ HyperTextAccessibleWrap::ApplyARIAState(aState);
+ aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
+
+ // If part of compound of XUL widget then pick up ARIA stuff from XUL widget
+ // element.
+ nsIContent* widgetElm = XULWidgetElm();
+ if (widgetElm)
+ aria::MapToState(aria::eARIAAutoComplete, widgetElm->AsElement(), aState);
+}
+
+uint64_t
+HTMLTextFieldAccessible::NativeState()
+{
+ uint64_t state = HyperTextAccessibleWrap::NativeState();
+
+ // Text fields are always editable, even if they are also read only or
+ // disabled.
+ state |= states::EDITABLE;
+
+ // can be focusable, focused, protected. readonly, unavailable, selected
+ if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::password, eIgnoreCase)) {
+ state |= states::PROTECTED;
+ }
+
+ if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
+ state |= states::READONLY;
+ }
+
+ // Is it an <input> or a <textarea> ?
+ HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
+ state |= input && input->IsSingleLineTextControl() ?
+ states::SINGLE_LINE : states::MULTI_LINE;
+
+ if (state & (states::PROTECTED | states::MULTI_LINE | states::READONLY |
+ states::UNAVAILABLE))
+ return state;
+
+ // Expose autocomplete states if this input is part of autocomplete widget.
+ Accessible* widget = ContainerWidget();
+ if (widget && widget-IsAutoComplete()) {
+ state |= states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION;
+ return state;
+ }
+
+ // Expose autocomplete state if it has associated autocomplete list.
+ if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::list))
+ return state | states::SUPPORTS_AUTOCOMPLETION | states::HASPOPUP;
+
+ // Ordinal XUL textboxes don't support autocomplete.
+ if (!XULWidgetElm() && Preferences::GetBool("browser.formfill.enable")) {
+ // Check to see if autocompletion is allowed on this input. We don't expose
+ // it for password fields even though the entire password can be remembered
+ // for a page if the user asks it to be. However, the kind of autocomplete
+ // we're talking here is based on what the user types, where a popup of
+ // possible choices comes up.
+ nsAutoString autocomplete;
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete,
+ autocomplete);
+
+ if (!autocomplete.LowerCaseEqualsLiteral("off")) {
+ nsIContent* formContent = input->GetFormElement();
+ if (formContent) {
+ formContent->GetAttr(kNameSpaceID_None,
+ nsGkAtoms::autocomplete, autocomplete);
+ }
+
+ if (!formContent || !autocomplete.LowerCaseEqualsLiteral("off"))
+ state |= states::SUPPORTS_AUTOCOMPLETION;
+ }
+ }
+
+ return state;
+}
+
+uint8_t
+HTMLTextFieldAccessible::ActionCount()
+{
+ return 1;
+}
+
+void
+HTMLTextFieldAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ if (aIndex == eAction_Click)
+ aName.AssignLiteral("activate");
+}
+
+bool
+HTMLTextFieldAccessible::DoAction(uint8_t aIndex)
+{
+ if (aIndex != 0)
+ return false;
+
+ TakeFocus();
+ return true;
+}
+
+already_AddRefed<nsIEditor>
+HTMLTextFieldAccessible::GetEditor() const
+{
+ nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mContent));
+ if (!editableElt)
+ return nullptr;
+
+ // nsGenericHTMLElement::GetEditor has a security check.
+ // Make sure we're not restricted by the permissions of
+ // whatever script is currently running.
+ mozilla::dom::AutoNoJSAPI nojsapi;
+
+ nsCOMPtr<nsIEditor> editor;
+ editableElt->GetEditor(getter_AddRefs(editor));
+
+ return editor.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTextFieldAccessible: Widgets
+
+bool
+HTMLTextFieldAccessible::IsWidget() const
+{
+ return true;
+}
+
+Accessible*
+HTMLTextFieldAccessible::ContainerWidget() const
+{
+ if (!mParent || mParent->Role() != roles::AUTOCOMPLETE) {
+ return nullptr;
+ }
+ return mParent;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLFileInputAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLFileInputAccessible::
+HTMLFileInputAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+ mType = eHTMLFileInputType;
+}
+
+role
+HTMLFileInputAccessible::NativeRole()
+{
+ // JAWS wants a text container, others don't mind. No specific role in
+ // AT APIs.
+ return roles::TEXT_CONTAINER;
+}
+
+nsresult
+HTMLFileInputAccessible::HandleAccEvent(AccEvent* aEvent)
+{
+ nsresult rv = HyperTextAccessibleWrap::HandleAccEvent(aEvent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Redirect state change events for inherited states to child controls. Note,
+ // unavailable state is not redirected. That's a standard for unavailable
+ // state handling.
+ AccStateChangeEvent* event = downcast_accEvent(aEvent);
+ if (event &&
+ (event->GetState() == states::BUSY ||
+ event->GetState() == states::REQUIRED ||
+ event->GetState() == states::HASPOPUP ||
+ event->GetState() == states::INVALID)) {
+ Accessible* button = GetChildAt(0);
+ if (button && button->Role() == roles::PUSHBUTTON) {
+ RefPtr<AccStateChangeEvent> childEvent =
+ new AccStateChangeEvent(button, event->GetState(),
+ event->IsStateEnabled(), event->FromUserInput());
+ nsEventShell::FireEvent(childEvent);
+ }
+ }
+
+ return NS_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSpinnerAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+role
+HTMLSpinnerAccessible::NativeRole()
+{
+ return roles::SPINBUTTON;
+}
+
+void
+HTMLSpinnerAccessible::Value(nsString& aValue)
+{
+ AccessibleWrap::Value(aValue);
+ if (!aValue.IsEmpty())
+ return;
+
+ HTMLInputElement::FromContent(mContent)->GetValue(aValue);
+}
+
+double
+HTMLSpinnerAccessible::MaxValue() const
+{
+ double value = AccessibleWrap::MaxValue();
+ if (!IsNaN(value))
+ return value;
+
+ return HTMLInputElement::FromContent(mContent)->GetMaximum().toDouble();
+}
+
+
+double
+HTMLSpinnerAccessible::MinValue() const
+{
+ double value = AccessibleWrap::MinValue();
+ if (!IsNaN(value))
+ return value;
+
+ return HTMLInputElement::FromContent(mContent)->GetMinimum().toDouble();
+}
+
+double
+HTMLSpinnerAccessible::Step() const
+{
+ double value = AccessibleWrap::Step();
+ if (!IsNaN(value))
+ return value;
+
+ return HTMLInputElement::FromContent(mContent)->GetStep().toDouble();
+}
+
+double
+HTMLSpinnerAccessible::CurValue() const
+{
+ double value = AccessibleWrap::CurValue();
+ if (!IsNaN(value))
+ return value;
+
+ return HTMLInputElement::FromContent(mContent)->GetValueAsDecimal().toDouble();
+}
+
+bool
+HTMLSpinnerAccessible::SetCurValue(double aValue)
+{
+ ErrorResult er;
+ HTMLInputElement::FromContent(mContent)->SetValueAsNumber(aValue, er);
+ return !er.Failed();
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLRangeAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+role
+HTMLRangeAccessible::NativeRole()
+{
+ return roles::SLIDER;
+}
+
+bool
+HTMLRangeAccessible::IsWidget() const
+{
+ return true;
+}
+
+void
+HTMLRangeAccessible::Value(nsString& aValue)
+{
+ LeafAccessible::Value(aValue);
+ if (!aValue.IsEmpty())
+ return;
+
+ HTMLInputElement::FromContent(mContent)->GetValue(aValue);
+}
+
+double
+HTMLRangeAccessible::MaxValue() const
+{
+ double value = LeafAccessible::MaxValue();
+ if (!IsNaN(value))
+ return value;
+
+ return HTMLInputElement::FromContent(mContent)->GetMaximum().toDouble();
+}
+
+double
+HTMLRangeAccessible::MinValue() const
+{
+ double value = LeafAccessible::MinValue();
+ if (!IsNaN(value))
+ return value;
+
+ return HTMLInputElement::FromContent(mContent)->GetMinimum().toDouble();
+}
+
+double
+HTMLRangeAccessible::Step() const
+{
+ double value = LeafAccessible::Step();
+ if (!IsNaN(value))
+ return value;
+
+ return HTMLInputElement::FromContent(mContent)->GetStep().toDouble();
+}
+
+double
+HTMLRangeAccessible::CurValue() const
+{
+ double value = LeafAccessible::CurValue();
+ if (!IsNaN(value))
+ return value;
+
+ return HTMLInputElement::FromContent(mContent)->GetValueAsDecimal().toDouble();
+}
+
+bool
+HTMLRangeAccessible::SetCurValue(double aValue)
+{
+ ErrorResult er;
+ HTMLInputElement::FromContent(mContent)->SetValueAsNumber(aValue, er);
+ return !er.Failed();
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLGroupboxAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLGroupboxAccessible::
+ HTMLGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+}
+
+role
+HTMLGroupboxAccessible::NativeRole()
+{
+ return roles::GROUPING;
+}
+
+nsIContent*
+HTMLGroupboxAccessible::GetLegend() const
+{
+ for (nsIContent* legendContent = mContent->GetFirstChild(); legendContent;
+ legendContent = legendContent->GetNextSibling()) {
+ if (legendContent->NodeInfo()->Equals(nsGkAtoms::legend,
+ mContent->GetNameSpaceID())) {
+ // Either XHTML namespace or no namespace
+ return legendContent;
+ }
+ }
+
+ return nullptr;
+}
+
+ENameValueFlag
+HTMLGroupboxAccessible::NativeName(nsString& aName)
+{
+ ENameValueFlag nameFlag = Accessible::NativeName(aName);
+ if (!aName.IsEmpty())
+ return nameFlag;
+
+ nsIContent* legendContent = GetLegend();
+ if (legendContent)
+ nsTextEquivUtils::AppendTextEquivFromContent(this, legendContent, &aName);
+
+ return eNameOK;
+}
+
+Relation
+HTMLGroupboxAccessible::RelationByType(RelationType aType)
+{
+ Relation rel = HyperTextAccessibleWrap::RelationByType(aType);
+ // No override for label, so use <legend> for this <fieldset>
+ if (aType == RelationType::LABELLED_BY)
+ rel.AppendTarget(mDoc, GetLegend());
+
+ return rel;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLLegendAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLLegendAccessible::
+ HTMLLegendAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+}
+
+Relation
+HTMLLegendAccessible::RelationByType(RelationType aType)
+{
+ Relation rel = HyperTextAccessibleWrap::RelationByType(aType);
+ if (aType != RelationType::LABEL_FOR)
+ return rel;
+
+ Accessible* groupbox = Parent();
+ if (groupbox && groupbox->Role() == roles::GROUPING)
+ rel.AppendTarget(groupbox);
+
+ return rel;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLFigureAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLFigureAccessible::
+ HTMLFigureAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+}
+
+ENameValueFlag
+HTMLFigureAccessible::NativeName(nsString& aName)
+{
+ ENameValueFlag nameFlag = HyperTextAccessibleWrap::NativeName(aName);
+ if (!aName.IsEmpty())
+ return nameFlag;
+
+ nsIContent* captionContent = Caption();
+ if (captionContent)
+ nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName);
+
+ return eNameOK;
+}
+
+Relation
+HTMLFigureAccessible::RelationByType(RelationType aType)
+{
+ Relation rel = HyperTextAccessibleWrap::RelationByType(aType);
+ if (aType == RelationType::LABELLED_BY)
+ rel.AppendTarget(mDoc, Caption());
+
+ return rel;
+}
+
+nsIContent*
+HTMLFigureAccessible::Caption() const
+{
+ for (nsIContent* childContent = mContent->GetFirstChild(); childContent;
+ childContent = childContent->GetNextSibling()) {
+ if (childContent->NodeInfo()->Equals(nsGkAtoms::figcaption,
+ mContent->GetNameSpaceID())) {
+ return childContent;
+ }
+ }
+
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLFigcaptionAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLFigcaptionAccessible::
+ HTMLFigcaptionAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+}
+
+Relation
+HTMLFigcaptionAccessible::RelationByType(RelationType aType)
+{
+ Relation rel = HyperTextAccessibleWrap::RelationByType(aType);
+ if (aType != RelationType::LABEL_FOR)
+ return rel;
+
+ Accessible* figure = Parent();
+ if (figure &&
+ figure->GetContent()->NodeInfo()->Equals(nsGkAtoms::figure,
+ mContent->GetNameSpaceID())) {
+ rel.AppendTarget(figure);
+ }
+
+ return rel;
+}
diff --git a/accessible/html/HTMLFormControlAccessible.h b/accessible/html/HTMLFormControlAccessible.h
new file mode 100644
index 0000000000..c2f30d53a7
--- /dev/null
+++ b/accessible/html/HTMLFormControlAccessible.h
@@ -0,0 +1,284 @@
+/* -*- 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_HTMLFormControlAccessible_H_
+#define MOZILLA_A11Y_HTMLFormControlAccessible_H_
+
+#include "FormControlAccessible.h"
+#include "HyperTextAccessibleWrap.h"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Accessible for HTML progress element.
+ */
+typedef ProgressMeterAccessible<1> HTMLProgressMeterAccessible;
+
+/**
+ * Accessible for HTML input@type="checkbox".
+ */
+class HTMLCheckboxAccessible : public LeafAccessible
+{
+
+public:
+ enum { eAction_Click = 0 };
+
+ HTMLCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ LeafAccessible(aContent, aDoc)
+ {
+ // Ignore "CheckboxStateChange" DOM event in lieu of document observer
+ // state change notification.
+ mStateFlags |= eIgnoreDOMUIEvent;
+ }
+
+ // 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;
+};
+
+
+/**
+ * Accessible for HTML input@type="radio" element.
+ */
+class HTMLRadioButtonAccessible : public RadioButtonAccessible
+{
+
+public:
+ HTMLRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ RadioButtonAccessible(aContent, aDoc)
+ {
+ // Ignore "RadioStateChange" DOM event in lieu of document observer
+ // state change notification.
+ mStateFlags |= eIgnoreDOMUIEvent;
+ }
+
+ // Accessible
+ virtual uint64_t NativeState() override;
+ virtual void GetPositionAndSizeInternal(int32_t *aPosInSet,
+ int32_t *aSetSize) override;
+};
+
+
+/**
+ * Accessible for HTML input@type="button", @type="submit", @type="image"
+ * and HTML button elements.
+ */
+class HTMLButtonAccessible : public HyperTextAccessibleWrap
+{
+
+public:
+ enum { eAction_Click = 0 };
+
+ HTMLButtonAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // Accessible
+ virtual mozilla::a11y::role NativeRole() override;
+ virtual uint64_t State() 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;
+
+protected:
+ // Accessible
+ virtual ENameValueFlag NativeName(nsString& aName) override;
+};
+
+
+/**
+ * Accessible for HTML input@type="text", input@type="password", textarea and
+ * other HTML text controls.
+ */
+class HTMLTextFieldAccessible final : public HyperTextAccessibleWrap
+{
+
+public:
+ enum { eAction_Click = 0 };
+
+ HTMLTextFieldAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // HyperTextAccessible
+ virtual already_AddRefed<nsIEditor> GetEditor() const override;
+
+ // Accessible
+ virtual void Value(nsString& aValue) override;
+ virtual void ApplyARIAState(uint64_t* aState) const override;
+ virtual mozilla::a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+ virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() 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 Accessible* ContainerWidget() const override;
+
+protected:
+ virtual ~HTMLTextFieldAccessible() {}
+
+ // Accessible
+ virtual ENameValueFlag NativeName(nsString& aName) override;
+
+ /**
+ * Return a XUL widget element this input is part of.
+ */
+ nsIContent* XULWidgetElm() const { return mContent->GetBindingParent(); }
+};
+
+
+/**
+ * Accessible for input@type="file" element.
+ */
+class HTMLFileInputAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLFileInputAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // Accessible
+ virtual mozilla::a11y::role NativeRole() override;
+ virtual nsresult HandleAccEvent(AccEvent* aAccEvent) override;
+};
+
+
+/**
+ * Used for HTML input@type="number".
+ */
+class HTMLSpinnerAccessible : public AccessibleWrap
+{
+public:
+ HTMLSpinnerAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ AccessibleWrap(aContent, aDoc)
+ {
+ mStateFlags |= eHasNumericValue;
+}
+
+ // Accessible
+ virtual mozilla::a11y::role NativeRole() override;
+ virtual void Value(nsString& aValue) override;
+
+ 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;
+};
+
+
+/**
+ * Used for input@type="range" element.
+ */
+class HTMLRangeAccessible : public LeafAccessible
+{
+public:
+ HTMLRangeAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ LeafAccessible(aContent, aDoc)
+ {
+ mStateFlags |= eHasNumericValue;
+ }
+
+ // Accessible
+ virtual void Value(nsString& aValue) override;
+ virtual mozilla::a11y::role NativeRole() 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;
+
+ // Widgets
+ virtual bool IsWidget() const override;
+};
+
+
+/**
+ * Accessible for HTML fieldset element.
+ */
+class HTMLGroupboxAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLGroupboxAccessible(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;
+
+ // HTMLGroupboxAccessible
+ nsIContent* GetLegend() const;
+};
+
+
+/**
+ * Accessible for HTML legend element.
+ */
+class HTMLLegendAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLLegendAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // Accessible
+ virtual Relation RelationByType(RelationType aType) override;
+};
+
+/**
+ * Accessible for HTML5 figure element.
+ */
+class HTMLFigureAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLFigureAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // Accessible
+ virtual Relation RelationByType(RelationType aType) override;
+
+protected:
+ // Accessible
+ virtual ENameValueFlag NativeName(nsString& aName) override;
+
+ // HTMLLegendAccessible
+ nsIContent* Caption() const;
+};
+
+
+/**
+ * Accessible for HTML5 figcaption element.
+ */
+class HTMLFigcaptionAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLFigcaptionAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // Accessible
+ virtual Relation RelationByType(RelationType aType) override;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/html/HTMLImageMapAccessible.cpp b/accessible/html/HTMLImageMapAccessible.cpp
new file mode 100644
index 0000000000..d6acbeba6a
--- /dev/null
+++ b/accessible/html/HTMLImageMapAccessible.cpp
@@ -0,0 +1,228 @@
+/* -*- 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 "HTMLImageMapAccessible.h"
+
+#include "ARIAMap.h"
+#include "nsAccUtils.h"
+#include "DocAccessible-inl.h"
+#include "Role.h"
+
+#include "nsIDOMHTMLCollection.h"
+#include "nsIServiceManager.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMHTMLAreaElement.h"
+#include "nsIFrame.h"
+#include "nsImageFrame.h"
+#include "nsImageMap.h"
+#include "nsIURI.h"
+
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLImageMapAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLImageMapAccessible::
+ HTMLImageMapAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ ImageAccessibleWrap(aContent, aDoc)
+{
+ mType = eImageMapType;
+
+ UpdateChildAreas(false);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLImageMapAccessible: nsISupports
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLImageMapAccessible, ImageAccessible)
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLImageMapAccessible: Accessible public
+
+role
+HTMLImageMapAccessible::NativeRole()
+{
+ return roles::IMAGE_MAP;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLImageMapAccessible: HyperLinkAccessible
+
+uint32_t
+HTMLImageMapAccessible::AnchorCount()
+{
+ return ChildCount();
+}
+
+Accessible*
+HTMLImageMapAccessible::AnchorAt(uint32_t aAnchorIndex)
+{
+ return GetChildAt(aAnchorIndex);
+}
+
+already_AddRefed<nsIURI>
+HTMLImageMapAccessible::AnchorURIAt(uint32_t aAnchorIndex)
+{
+ Accessible* area = GetChildAt(aAnchorIndex);
+ if (!area)
+ return nullptr;
+
+ nsIContent* linkContent = area->GetContent();
+ return linkContent ? linkContent->GetHrefURI() : nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLImageMapAccessible: public
+
+void
+HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
+{
+ nsImageFrame* imageFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+
+ // If image map is not initialized yet then we trigger one time more later.
+ nsImageMap* imageMapObj = imageFrame->GetExistingImageMap();
+ if (!imageMapObj)
+ return;
+
+ TreeMutation mt(this, TreeMutation::kNoEvents & !aDoFireEvents);
+
+ // Remove areas that are not a valid part of the image map anymore.
+ for (int32_t childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) {
+ Accessible* area = mChildren.ElementAt(childIdx);
+ if (area->GetContent()->GetPrimaryFrame())
+ continue;
+
+ mt.BeforeRemoval(area);
+ RemoveChild(area);
+ }
+
+ // Insert new areas into the tree.
+ uint32_t areaElmCount = imageMapObj->AreaCount();
+ for (uint32_t idx = 0; idx < areaElmCount; idx++) {
+ nsIContent* areaContent = imageMapObj->GetAreaAt(idx);
+ Accessible* area = mChildren.SafeElementAt(idx);
+ if (!area || area->GetContent() != areaContent) {
+ RefPtr<Accessible> area = new HTMLAreaAccessible(areaContent, mDoc);
+ mDoc->BindToDocument(area, aria::GetRoleMap(areaContent->AsElement()));
+
+ if (!InsertChildAt(idx, area)) {
+ mDoc->UnbindFromDocument(area);
+ break;
+ }
+
+ mt.AfterInsertion(area);
+ }
+ }
+
+ mt.Done();
+}
+
+Accessible*
+HTMLImageMapAccessible::GetChildAccessibleFor(const nsINode* aNode) const
+{
+ uint32_t length = mChildren.Length();
+ for (uint32_t i = 0; i < length; i++) {
+ Accessible* area = mChildren[i];
+ if (area->GetContent() == aNode)
+ return area;
+ }
+
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLAreaAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLAreaAccessible::
+ HTMLAreaAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HTMLLinkAccessible(aContent, aDoc)
+{
+ // Make HTML area DOM element not accessible. HTML image map accessible
+ // manages its tree itself.
+ mStateFlags |= eNotNodeMapEntry;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLAreaAccessible: Accessible
+
+ENameValueFlag
+HTMLAreaAccessible::NativeName(nsString& aName)
+{
+ ENameValueFlag nameFlag = Accessible::NativeName(aName);
+ if (!aName.IsEmpty())
+ return nameFlag;
+
+ if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName))
+ Value(aName);
+
+ return eNameOK;
+}
+
+void
+HTMLAreaAccessible::Description(nsString& aDescription)
+{
+ aDescription.Truncate();
+
+ // Still to do - follow IE's standard here
+ nsCOMPtr<nsIDOMHTMLAreaElement> area(do_QueryInterface(mContent));
+ if (area)
+ area->GetShape(aDescription);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLAreaAccessible: Accessible public
+
+Accessible*
+HTMLAreaAccessible::ChildAtPoint(int32_t aX, int32_t aY,
+ EWhichChildAtPoint aWhichChild)
+{
+ // Don't walk into area accessibles.
+ return this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLImageMapAccessible: HyperLinkAccessible
+
+uint32_t
+HTMLAreaAccessible::StartOffset()
+{
+ // Image map accessible is not hypertext accessible therefore
+ // StartOffset/EndOffset implementations of Accessible doesn't work here.
+ // We return index in parent because image map contains area links only which
+ // are embedded objects.
+ // XXX: image map should be a hypertext accessible.
+ return IndexInParent();
+}
+
+uint32_t
+HTMLAreaAccessible::EndOffset()
+{
+ return IndexInParent() + 1;
+}
+
+nsRect
+HTMLAreaAccessible::RelativeBounds(nsIFrame** aBoundingFrame) const
+{
+ nsIFrame* frame = GetFrame();
+ if (!frame)
+ return nsRect();
+
+ nsImageFrame* imageFrame = do_QueryFrame(frame);
+ nsImageMap* map = imageFrame->GetImageMap();
+
+ nsRect bounds;
+ nsresult rv = map->GetBoundsForAreaContent(mContent, bounds);
+ if (NS_FAILED(rv))
+ return nsRect();
+
+ // XXX Areas are screwy; they return their rects as a pair of points, one pair
+ // stored into the width and height.
+ *aBoundingFrame = frame;
+ bounds.width -= bounds.x;
+ bounds.height -= bounds.y;
+ return bounds;
+}
diff --git a/accessible/html/HTMLImageMapAccessible.h b/accessible/html/HTMLImageMapAccessible.h
new file mode 100644
index 0000000000..1668245dc4
--- /dev/null
+++ b/accessible/html/HTMLImageMapAccessible.h
@@ -0,0 +1,89 @@
+/* -*- 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_HTMLImageMapAccessible_h__
+#define mozilla_a11y_HTMLImageMapAccessible_h__
+
+#include "HTMLLinkAccessible.h"
+#include "ImageAccessibleWrap.h"
+#include "nsIDOMHTMLMapElement.h"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Used for HTML image maps.
+ */
+class HTMLImageMapAccessible final : public ImageAccessibleWrap
+{
+public:
+ HTMLImageMapAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // nsISupports and cycle collector
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+
+ // HyperLinkAccessible
+ virtual uint32_t AnchorCount() override;
+ virtual Accessible* AnchorAt(uint32_t aAnchorIndex) override;
+ virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex) override;
+
+ /**
+ * Update area children of the image map.
+ */
+ void UpdateChildAreas(bool aDoFireEvents = true);
+
+ /**
+ * Return accessible of child node.
+ */
+ Accessible* GetChildAccessibleFor(const nsINode* aNode) const;
+
+protected:
+ virtual ~HTMLImageMapAccessible() { }
+};
+
+/**
+ * Accessible for image map areas - must be child of image.
+ */
+class HTMLAreaAccessible final : public HTMLLinkAccessible
+{
+public:
+
+ HTMLAreaAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // Accessible
+ virtual void Description(nsString& aDescription) override;
+ virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
+ EWhichChildAtPoint aWhichChild) override;
+ virtual nsRect RelativeBounds(nsIFrame** aBoundingFrame) const override;
+
+ // HyperLinkAccessible
+ virtual uint32_t StartOffset() override;
+ virtual uint32_t EndOffset() override;
+
+ virtual bool IsAcceptableChild(nsIContent* aEl) const override
+ { return false; }
+
+protected:
+ // Accessible
+ virtual ENameValueFlag NativeName(nsString& aName) override;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Accessible downcasting method
+
+inline HTMLImageMapAccessible*
+Accessible::AsImageMap()
+{
+ return IsImageMap() ? static_cast<HTMLImageMapAccessible*>(this) : nullptr;
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/html/HTMLLinkAccessible.cpp b/accessible/html/HTMLLinkAccessible.cpp
new file mode 100644
index 0000000000..6172c857b3
--- /dev/null
+++ b/accessible/html/HTMLLinkAccessible.cpp
@@ -0,0 +1,147 @@
+/* -*- 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 "HTMLLinkAccessible.h"
+
+#include "nsCoreUtils.h"
+#include "DocAccessible.h"
+#include "Role.h"
+#include "States.h"
+
+#include "nsContentUtils.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLLinkAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLLinkAccessible::
+ HTMLLinkAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLLinkAccessible, HyperTextAccessible)
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIAccessible
+
+role
+HTMLLinkAccessible::NativeRole()
+{
+ return roles::LINK;
+}
+
+uint64_t
+HTMLLinkAccessible::NativeState()
+{
+ return HyperTextAccessibleWrap::NativeState() & ~states::READONLY;
+}
+
+uint64_t
+HTMLLinkAccessible::NativeLinkState() const
+{
+ EventStates eventState = mContent->AsElement()->State();
+ if (eventState.HasState(NS_EVENT_STATE_UNVISITED))
+ return states::LINKED;
+
+ if (eventState.HasState(NS_EVENT_STATE_VISITED))
+ return states::LINKED | states::TRAVERSED;
+
+ // This is a either named anchor (a link with also a name attribute) or
+ // it doesn't have any attributes. Check if 'click' event handler is
+ // registered, otherwise bail out.
+ return nsCoreUtils::HasClickListener(mContent) ? states::LINKED : 0;
+}
+
+uint64_t
+HTMLLinkAccessible::NativeInteractiveState() const
+{
+ uint64_t state = HyperTextAccessibleWrap::NativeInteractiveState();
+
+ // This is how we indicate it is a named anchor. In other words, this anchor
+ // can be selected as a location :) There is no other better state to use to
+ // indicate this.
+ if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::name))
+ state |= states::SELECTABLE;
+
+ return state;
+}
+
+void
+HTMLLinkAccessible::Value(nsString& aValue)
+{
+ aValue.Truncate();
+
+ HyperTextAccessible::Value(aValue);
+ if (aValue.IsEmpty())
+ nsContentUtils::GetLinkLocation(mContent->AsElement(), aValue);
+}
+
+uint8_t
+HTMLLinkAccessible::ActionCount()
+{
+ return IsLinked() ? 1 : HyperTextAccessible::ActionCount();
+}
+
+void
+HTMLLinkAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ aName.Truncate();
+
+ if (!IsLinked()) {
+ HyperTextAccessible::ActionNameAt(aIndex, aName);
+ return;
+ }
+
+ // Action 0 (default action): Jump to link
+ if (aIndex == eAction_Jump)
+ aName.AssignLiteral("jump");
+}
+
+bool
+HTMLLinkAccessible::DoAction(uint8_t aIndex)
+{
+ if (!IsLinked())
+ return HyperTextAccessible::DoAction(aIndex);
+
+ // Action 0 (default action): Jump to link
+ if (aIndex != eAction_Jump)
+ return false;
+
+ DoCommand();
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HyperLinkAccessible
+
+bool
+HTMLLinkAccessible::IsLink()
+{
+ // Expose HyperLinkAccessible unconditionally.
+ return true;
+}
+
+already_AddRefed<nsIURI>
+HTMLLinkAccessible::AnchorURIAt(uint32_t aAnchorIndex)
+{
+ return aAnchorIndex == 0 ? mContent->GetHrefURI() : nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Protected members
+
+bool
+HTMLLinkAccessible::IsLinked() const
+{
+ EventStates state = mContent->AsElement()->State();
+ return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED |
+ NS_EVENT_STATE_UNVISITED);
+}
diff --git a/accessible/html/HTMLLinkAccessible.h b/accessible/html/HTMLLinkAccessible.h
new file mode 100644
index 0000000000..aa4da22be5
--- /dev/null
+++ b/accessible/html/HTMLLinkAccessible.h
@@ -0,0 +1,51 @@
+/* -*- 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_HTMLLinkAccessible_h__
+#define mozilla_a11y_HTMLLinkAccessible_h__
+
+#include "HyperTextAccessibleWrap.h"
+
+namespace mozilla {
+namespace a11y {
+
+class HTMLLinkAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLLinkAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ virtual void Value(nsString& aValue) override;
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+ virtual uint64_t NativeLinkState() const 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;
+
+ // HyperLinkAccessible
+ virtual bool IsLink() override;
+ virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex) override;
+
+protected:
+ virtual ~HTMLLinkAccessible() {}
+
+ enum { eAction_Jump = 0 };
+
+ /**
+ * Returns true if the link has href attribute.
+ */
+ bool IsLinked() const;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/html/HTMLListAccessible.cpp b/accessible/html/HTMLListAccessible.cpp
new file mode 100644
index 0000000000..d5de257181
--- /dev/null
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -0,0 +1,199 @@
+/* -*- 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 "HTMLListAccessible.h"
+
+#include "DocAccessible.h"
+#include "nsAccUtils.h"
+#include "Role.h"
+#include "States.h"
+
+#include "nsBlockFrame.h"
+#include "nsBulletFrame.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLListAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLListAccessible, HyperTextAccessible)
+
+role
+HTMLListAccessible::NativeRole()
+{
+ a11y::role r = GetAccService()->MarkupRole(mContent);
+ return r != roles::NOTHING ? r : roles::LIST;
+}
+
+uint64_t
+HTMLListAccessible::NativeState()
+{
+ return HyperTextAccessibleWrap::NativeState() | states::READONLY;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLLIAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLLIAccessible::
+ HTMLLIAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc), mBullet(nullptr)
+{
+ mType = eHTMLLiType;
+
+ nsBlockFrame* blockFrame = do_QueryFrame(GetFrame());
+ if (blockFrame && blockFrame->HasBullet()) {
+ mBullet = new HTMLListBulletAccessible(mContent, mDoc);
+ Document()->BindToDocument(mBullet, nullptr);
+ AppendChild(mBullet);
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLLIAccessible, HyperTextAccessible)
+
+void
+HTMLLIAccessible::Shutdown()
+{
+ mBullet = nullptr;
+
+ HyperTextAccessibleWrap::Shutdown();
+}
+
+role
+HTMLLIAccessible::NativeRole()
+{
+ a11y::role r = GetAccService()->MarkupRole(mContent);
+ return r != roles::NOTHING ? r : roles::LISTITEM;
+}
+
+uint64_t
+HTMLLIAccessible::NativeState()
+{
+ return HyperTextAccessibleWrap::NativeState() | states::READONLY;
+}
+
+nsIntRect
+HTMLLIAccessible::Bounds() const
+{
+ nsIntRect rect = AccessibleWrap::Bounds();
+ if (rect.IsEmpty() || !mBullet || mBullet->IsInside())
+ return rect;
+
+ nsIntRect bulletRect = mBullet->Bounds();
+
+ rect.width += rect.x - bulletRect.x;
+ rect.x = bulletRect.x; // Move x coordinate of list item over to cover bullet as well
+ return rect;
+}
+
+bool
+HTMLLIAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
+{
+ // Adjust index if there's a bullet.
+ if (mBullet && aIndex == 0 && aChild != mBullet) {
+ return HyperTextAccessible::InsertChildAt(aIndex + 1, aChild);
+ }
+
+ return HyperTextAccessible::InsertChildAt(aIndex, aChild);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLLIAccessible: public
+
+void
+HTMLLIAccessible::UpdateBullet(bool aHasBullet)
+{
+ if (aHasBullet == !!mBullet) {
+ NS_NOTREACHED("Bullet and accessible are in sync already!");
+ return;
+ }
+
+ TreeMutation mt(this);
+ if (aHasBullet) {
+ mBullet = new HTMLListBulletAccessible(mContent, mDoc);
+ mDoc->BindToDocument(mBullet, nullptr);
+ InsertChildAt(0, mBullet);
+ mt.AfterInsertion(mBullet);
+ }
+ else {
+ mt.BeforeRemoval(mBullet);
+ RemoveChild(mBullet);
+ mBullet = nullptr;
+ }
+ mt.Done();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLListBulletAccessible
+////////////////////////////////////////////////////////////////////////////////
+HTMLListBulletAccessible::
+ HTMLListBulletAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ LeafAccessible(aContent, aDoc)
+{
+ mGenericTypes |= eText;
+ mStateFlags |= eSharedNode;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLListBulletAccessible: Accessible
+
+nsIFrame*
+HTMLListBulletAccessible::GetFrame() const
+{
+ nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ return blockFrame ? blockFrame->GetBullet() : nullptr;
+}
+
+ENameValueFlag
+HTMLListBulletAccessible::Name(nsString &aName)
+{
+ aName.Truncate();
+
+ // Native anonymous content, ARIA can't be used. Get list bullet text.
+ nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (blockFrame) {
+ blockFrame->GetSpokenBulletText(aName);
+ }
+
+ return eNameOK;
+}
+
+role
+HTMLListBulletAccessible::NativeRole()
+{
+ return roles::STATICTEXT;
+}
+
+uint64_t
+HTMLListBulletAccessible::NativeState()
+{
+ return LeafAccessible::NativeState() | states::READONLY;
+}
+
+void
+HTMLListBulletAccessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
+ uint32_t aLength)
+{
+ nsAutoString bulletText;
+ nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (blockFrame)
+ blockFrame->GetSpokenBulletText(bulletText);
+
+ aText.Append(Substring(bulletText, aStartOffset, aLength));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLListBulletAccessible: public
+
+bool
+HTMLListBulletAccessible::IsInside() const
+{
+ nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ return blockFrame ? blockFrame->HasInsideBullet() : false;
+}
diff --git a/accessible/html/HTMLListAccessible.h b/accessible/html/HTMLListAccessible.h
new file mode 100644
index 0000000000..ad4a2d425e
--- /dev/null
+++ b/accessible/html/HTMLListAccessible.h
@@ -0,0 +1,105 @@
+/* -*- 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_HTMLListAccessible_h__
+#define mozilla_a11y_HTMLListAccessible_h__
+
+#include "BaseAccessibles.h"
+#include "HyperTextAccessibleWrap.h"
+
+namespace mozilla {
+namespace a11y {
+
+class HTMLListBulletAccessible;
+
+/**
+ * Used for HTML list (like HTML ul).
+ */
+class HTMLListAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLListAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc) { mGenericTypes |= eList; }
+
+ // nsISupports
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+
+protected:
+ virtual ~HTMLListAccessible() { }
+};
+
+
+/**
+ * Used for HTML list item (e.g. HTML li).
+ */
+class HTMLLIAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLLIAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ virtual void Shutdown() override;
+ virtual nsIntRect Bounds() const override;
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+
+ virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override;
+
+ // HTMLLIAccessible
+ HTMLListBulletAccessible* Bullet() const { return mBullet; }
+ void UpdateBullet(bool aHasBullet);
+
+protected:
+ virtual ~HTMLLIAccessible() { }
+
+private:
+ RefPtr<HTMLListBulletAccessible> mBullet;
+};
+
+
+/**
+ * Used for bullet of HTML list item element (for example, HTML li).
+ */
+class HTMLListBulletAccessible : public LeafAccessible
+{
+public:
+ HTMLListBulletAccessible(nsIContent* aContent, DocAccessible* aDoc);
+ virtual ~HTMLListBulletAccessible() { }
+
+ // Accessible
+ virtual nsIFrame* GetFrame() const override;
+ virtual ENameValueFlag Name(nsString& aName) override;
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+ virtual void AppendTextTo(nsAString& aText, uint32_t aStartOffset = 0,
+ uint32_t aLength = UINT32_MAX) override;
+
+ // HTMLListBulletAccessible
+
+ /**
+ * Return true if the bullet is inside of list item element boundaries.
+ */
+ bool IsInside() const;
+};
+
+
+inline HTMLLIAccessible*
+Accessible::AsHTMLListItem()
+{
+ return IsHTMLListItem() ? static_cast<HTMLLIAccessible*>(this) : nullptr;
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/html/HTMLSelectAccessible.cpp b/accessible/html/HTMLSelectAccessible.cpp
new file mode 100644
index 0000000000..cb98a0038a
--- /dev/null
+++ b/accessible/html/HTMLSelectAccessible.cpp
@@ -0,0 +1,610 @@
+/* -*- 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 "HTMLSelectAccessible.h"
+
+#include "Accessible-inl.h"
+#include "nsAccessibilityService.h"
+#include "nsAccUtils.h"
+#include "DocAccessible.h"
+#include "nsEventShell.h"
+#include "nsTextEquivUtils.h"
+#include "Role.h"
+#include "States.h"
+
+#include "nsCOMPtr.h"
+#include "mozilla/dom/HTMLOptionElement.h"
+#include "mozilla/dom/HTMLSelectElement.h"
+#include "nsIComboboxControlFrame.h"
+#include "nsContainerFrame.h"
+#include "nsIListControlFrame.h"
+
+using namespace mozilla::a11y;
+using namespace mozilla::dom;
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSelectListAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLSelectListAccessible::
+ HTMLSelectListAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ AccessibleWrap(aContent, aDoc)
+{
+ mGenericTypes |= eListControl | eSelect;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSelectListAccessible: Accessible public
+
+uint64_t
+HTMLSelectListAccessible::NativeState()
+{
+ uint64_t state = AccessibleWrap::NativeState();
+ if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple))
+ state |= states::MULTISELECTABLE | states::EXTSELECTABLE;
+
+ return state;
+}
+
+role
+HTMLSelectListAccessible::NativeRole()
+{
+ return roles::LISTBOX;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSelectListAccessible: SelectAccessible
+
+bool
+HTMLSelectListAccessible::SelectAll()
+{
+ return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
+ AccessibleWrap::SelectAll() : false;
+}
+
+bool
+HTMLSelectListAccessible::UnselectAll()
+{
+ return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
+ AccessibleWrap::UnselectAll() : false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSelectListAccessible: Widgets
+
+bool
+HTMLSelectListAccessible::IsWidget() const
+{
+ return true;
+}
+
+bool
+HTMLSelectListAccessible::IsActiveWidget() const
+{
+ return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+HTMLSelectListAccessible::AreItemsOperable() const
+{
+ return true;
+}
+
+Accessible*
+HTMLSelectListAccessible::CurrentItem()
+{
+ nsIListControlFrame* listControlFrame = do_QueryFrame(GetFrame());
+ if (listControlFrame) {
+ nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
+ if (activeOptionNode) {
+ DocAccessible* document = Document();
+ if (document)
+ return document->GetAccessible(activeOptionNode);
+ }
+ }
+ return nullptr;
+}
+
+void
+HTMLSelectListAccessible::SetCurrentItem(Accessible* aItem)
+{
+ aItem->GetContent()->SetAttr(kNameSpaceID_None,
+ nsGkAtoms::selected, NS_LITERAL_STRING("true"),
+ true);
+}
+
+bool
+HTMLSelectListAccessible::IsAcceptableChild(nsIContent* aEl) const
+{
+ return aEl->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSelectOptionAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLSelectOptionAccessible::
+ HTMLSelectOptionAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSelectOptionAccessible: Accessible public
+
+role
+HTMLSelectOptionAccessible::NativeRole()
+{
+ if (GetCombobox())
+ return roles::COMBOBOX_OPTION;
+
+ return roles::OPTION;
+}
+
+ENameValueFlag
+HTMLSelectOptionAccessible::NativeName(nsString& aName)
+{
+ // CASE #1 -- great majority of the cases
+ // find the label attribute - this is what the W3C says we should use
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
+ if (!aName.IsEmpty())
+ return eNameOK;
+
+ // CASE #2 -- no label parameter, get the first child,
+ // use it if it is a text node
+ nsIContent* text = mContent->GetFirstChild();
+ if (text && text->IsNodeOfType(nsINode::eTEXT)) {
+ nsTextEquivUtils::AppendTextEquivFromTextContent(text, &aName);
+ aName.CompressWhitespace();
+ return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
+ }
+
+ return eNameOK;
+}
+
+uint64_t
+HTMLSelectOptionAccessible::NativeState()
+{
+ // As a HTMLSelectOptionAccessible we can have the following states:
+ // SELECTABLE, SELECTED, FOCUSED, FOCUSABLE, OFFSCREEN
+ // Upcall to Accessible, but skip HyperTextAccessible impl
+ // because we don't want EDITABLE or SELECTABLE_TEXT
+ uint64_t state = Accessible::NativeState();
+
+ Accessible* select = GetSelect();
+ if (!select)
+ return state;
+
+ uint64_t selectState = select->State();
+ if (selectState & states::INVISIBLE)
+ return state;
+
+ // Are we selected?
+ HTMLOptionElement* option = HTMLOptionElement::FromContent(mContent);
+ bool selected = option && option->Selected();
+ if (selected)
+ state |= states::SELECTED;
+
+ if (selectState & states::OFFSCREEN) {
+ state |= states::OFFSCREEN;
+ } else if (selectState & states::COLLAPSED) {
+ // <select> is COLLAPSED: add OFFSCREEN, if not the currently
+ // visible option
+ if (!selected) {
+ state |= states::OFFSCREEN;
+ state ^= states::INVISIBLE;
+ } else {
+ // Clear offscreen and invisible for currently showing option
+ state &= ~(states::OFFSCREEN | states::INVISIBLE);
+ state |= selectState & states::OPAQUE1;
+ }
+ } else {
+ // XXX list frames are weird, don't rely on Accessible's general
+ // visibility implementation unless they get reimplemented in layout
+ state &= ~states::OFFSCREEN;
+ // <select> is not collapsed: compare bounds to calculate OFFSCREEN
+ Accessible* listAcc = Parent();
+ if (listAcc) {
+ nsIntRect optionRect = Bounds();
+ nsIntRect listRect = listAcc->Bounds();
+ if (optionRect.y < listRect.y ||
+ optionRect.y + optionRect.height > listRect.y + listRect.height) {
+ state |= states::OFFSCREEN;
+ }
+ }
+ }
+
+ return state;
+}
+
+uint64_t
+HTMLSelectOptionAccessible::NativeInteractiveState() const
+{
+ return NativelyUnavailable() ?
+ states::UNAVAILABLE : states::FOCUSABLE | states::SELECTABLE;
+}
+
+int32_t
+HTMLSelectOptionAccessible::GetLevelInternal()
+{
+ nsIContent* parentContent = mContent->GetParent();
+
+ int32_t level =
+ parentContent->NodeInfo()->Equals(nsGkAtoms::optgroup) ? 2 : 1;
+
+ if (level == 1 && Role() != roles::HEADING)
+ level = 0; // In a single level list, the level is irrelevant
+
+ return level;
+}
+
+nsRect
+HTMLSelectOptionAccessible::RelativeBounds(nsIFrame** aBoundingFrame) const
+{
+ Accessible* combobox = GetCombobox();
+ if (combobox && (combobox->State() & states::COLLAPSED))
+ return combobox->RelativeBounds(aBoundingFrame);
+
+ return HyperTextAccessibleWrap::RelativeBounds(aBoundingFrame);
+}
+
+void
+HTMLSelectOptionAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ if (aIndex == eAction_Select)
+ aName.AssignLiteral("select");
+}
+
+uint8_t
+HTMLSelectOptionAccessible::ActionCount()
+{
+ return 1;
+}
+
+bool
+HTMLSelectOptionAccessible::DoAction(uint8_t aIndex)
+{
+ if (aIndex != eAction_Select)
+ return false;
+
+ DoCommand();
+ return true;
+}
+
+void
+HTMLSelectOptionAccessible::SetSelected(bool aSelect)
+{
+ HTMLOptionElement* option = HTMLOptionElement::FromContent(mContent);
+ if (option)
+ option->SetSelected(aSelect);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSelectOptionAccessible: Widgets
+
+Accessible*
+HTMLSelectOptionAccessible::ContainerWidget() const
+{
+ Accessible* parent = Parent();
+ if (parent && parent->IsHTMLOptGroup())
+ parent = parent->Parent();
+
+ return parent && parent->IsListControl() ? parent : nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSelectOptGroupAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+role
+HTMLSelectOptGroupAccessible::NativeRole()
+{
+ return roles::GROUPING;
+}
+
+uint64_t
+HTMLSelectOptGroupAccessible::NativeInteractiveState() const
+{
+ return NativelyUnavailable() ? states::UNAVAILABLE : 0;
+}
+
+uint8_t
+HTMLSelectOptGroupAccessible::ActionCount()
+{
+ return 0;
+}
+
+void
+HTMLSelectOptGroupAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ aName.Truncate();
+}
+
+bool
+HTMLSelectOptGroupAccessible::DoAction(uint8_t aIndex)
+{
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLComboboxAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLComboboxAccessible::
+ HTMLComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ AccessibleWrap(aContent, aDoc)
+{
+ mType = eHTMLComboboxType;
+ mGenericTypes |= eCombobox;
+ mStateFlags |= eNoKidsFromDOM;
+
+ nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
+ if (comboFrame) {
+ nsIFrame* listFrame = comboFrame->GetDropDown();
+ if (listFrame) {
+ mListAccessible = new HTMLComboboxListAccessible(mParent, mContent, mDoc);
+ Document()->BindToDocument(mListAccessible, nullptr);
+ AppendChild(mListAccessible);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLComboboxAccessible: Accessible
+
+role
+HTMLComboboxAccessible::NativeRole()
+{
+ return roles::COMBOBOX;
+}
+
+bool
+HTMLComboboxAccessible::RemoveChild(Accessible* aChild)
+{
+ MOZ_ASSERT(aChild == mListAccessible);
+ if (AccessibleWrap::RemoveChild(aChild)) {
+ mListAccessible = nullptr;
+ return true;
+ }
+ return false;
+}
+
+void
+HTMLComboboxAccessible::Shutdown()
+{
+ MOZ_ASSERT(mDoc->IsDefunct() || !mListAccessible);
+ if (mListAccessible) {
+ mListAccessible->Shutdown();
+ mListAccessible = nullptr;
+ }
+
+ AccessibleWrap::Shutdown();
+}
+
+uint64_t
+HTMLComboboxAccessible::NativeState()
+{
+ // As a HTMLComboboxAccessible we can have the following states:
+ // FOCUSED, FOCUSABLE, HASPOPUP, EXPANDED, COLLAPSED
+ // Get focus status from base class
+ uint64_t state = Accessible::NativeState();
+
+ nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
+ if (comboFrame && comboFrame->IsDroppedDown())
+ state |= states::EXPANDED;
+ else
+ state |= states::COLLAPSED;
+
+ state |= states::HASPOPUP;
+ return state;
+}
+
+void
+HTMLComboboxAccessible::Description(nsString& aDescription)
+{
+ aDescription.Truncate();
+ // First check to see if combo box itself has a description, perhaps through
+ // tooltip (title attribute) or via aria-describedby
+ Accessible::Description(aDescription);
+ if (!aDescription.IsEmpty())
+ return;
+
+ // Otherwise use description of selected option.
+ Accessible* option = SelectedOption();
+ if (option)
+ option->Description(aDescription);
+}
+
+void
+HTMLComboboxAccessible::Value(nsString& aValue)
+{
+ // Use accessible name of selected option.
+ Accessible* option = SelectedOption();
+ if (option)
+ option->Name(aValue);
+}
+
+uint8_t
+HTMLComboboxAccessible::ActionCount()
+{
+ return 1;
+}
+
+bool
+HTMLComboboxAccessible::DoAction(uint8_t aIndex)
+{
+ if (aIndex != eAction_Click)
+ return false;
+
+ DoCommand();
+ return true;
+}
+
+void
+HTMLComboboxAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+ if (aIndex != HTMLComboboxAccessible::eAction_Click)
+ return;
+
+ nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
+ if (!comboFrame)
+ return;
+
+ if (comboFrame->IsDroppedDown())
+ aName.AssignLiteral("close");
+ else
+ aName.AssignLiteral("open");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLComboboxAccessible: Widgets
+
+bool
+HTMLComboboxAccessible::IsWidget() const
+{
+ return true;
+}
+
+bool
+HTMLComboboxAccessible::IsActiveWidget() const
+{
+ return FocusMgr()->HasDOMFocus(mContent);
+}
+
+bool
+HTMLComboboxAccessible::AreItemsOperable() const
+{
+ nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(GetFrame());
+ return comboboxFrame && comboboxFrame->IsDroppedDown();
+}
+
+Accessible*
+HTMLComboboxAccessible::CurrentItem()
+{
+ return AreItemsOperable() ? mListAccessible->CurrentItem() : nullptr;
+}
+
+void
+HTMLComboboxAccessible::SetCurrentItem(Accessible* aItem)
+{
+ if (AreItemsOperable())
+ mListAccessible->SetCurrentItem(aItem);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLComboboxAccessible: protected
+
+Accessible*
+HTMLComboboxAccessible::SelectedOption() const
+{
+ HTMLSelectElement* select = HTMLSelectElement::FromContent(mContent);
+ int32_t selectedIndex = select->SelectedIndex();
+
+ if (selectedIndex >= 0) {
+ HTMLOptionElement* option = select->Item(selectedIndex);
+ if (option) {
+ DocAccessible* document = Document();
+ if (document)
+ return document->GetAccessible(option);
+ }
+ }
+
+ return nullptr;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLComboboxListAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLComboboxListAccessible::
+ HTMLComboboxListAccessible(Accessible* aParent, nsIContent* aContent,
+ DocAccessible* aDoc) :
+ HTMLSelectListAccessible(aContent, aDoc)
+{
+ mStateFlags |= eSharedNode;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLComboboxAccessible: Accessible
+
+nsIFrame*
+HTMLComboboxListAccessible::GetFrame() const
+{
+ nsIFrame* frame = HTMLSelectListAccessible::GetFrame();
+ nsIComboboxControlFrame* comboBox = do_QueryFrame(frame);
+ if (comboBox) {
+ return comboBox->GetDropDown();
+ }
+
+ return nullptr;
+}
+
+role
+HTMLComboboxListAccessible::NativeRole()
+{
+ return roles::COMBOBOX_LIST;
+}
+
+uint64_t
+HTMLComboboxListAccessible::NativeState()
+{
+ // As a HTMLComboboxListAccessible we can have the following states:
+ // FOCUSED, FOCUSABLE, FLOATING, INVISIBLE
+ // Get focus status from base class
+ uint64_t state = Accessible::NativeState();
+
+ nsIComboboxControlFrame* comboFrame = do_QueryFrame(mParent->GetFrame());
+ if (comboFrame && comboFrame->IsDroppedDown())
+ state |= states::FLOATING;
+ else
+ state |= states::INVISIBLE;
+
+ return state;
+}
+
+nsRect
+HTMLComboboxListAccessible::RelativeBounds(nsIFrame** aBoundingFrame) const
+{
+ *aBoundingFrame = nullptr;
+
+ Accessible* comboAcc = Parent();
+ if (!comboAcc)
+ return nsRect();
+
+ if (0 == (comboAcc->State() & states::COLLAPSED)) {
+ return HTMLSelectListAccessible::RelativeBounds(aBoundingFrame);
+ }
+
+ // Get the first option.
+ nsIContent* content = mContent->GetFirstChild();
+ if (!content)
+ return nsRect();
+
+ nsIFrame* frame = content->GetPrimaryFrame();
+ if (!frame) {
+ *aBoundingFrame = nullptr;
+ return nsRect();
+ }
+
+ *aBoundingFrame = frame->GetParent();
+ return (*aBoundingFrame)->GetRect();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLComboboxListAccessible: Widgets
+
+bool
+HTMLComboboxListAccessible::IsActiveWidget() const
+{
+ return mParent && mParent->IsActiveWidget();
+}
+
+bool
+HTMLComboboxListAccessible::AreItemsOperable() const
+{
+ return mParent && mParent->AreItemsOperable();
+}
+
diff --git a/accessible/html/HTMLSelectAccessible.h b/accessible/html/HTMLSelectAccessible.h
new file mode 100644
index 0000000000..0c781034fb
--- /dev/null
+++ b/accessible/html/HTMLSelectAccessible.h
@@ -0,0 +1,222 @@
+/* -*- 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_HTMLSelectAccessible_h__
+#define mozilla_a11y_HTMLSelectAccessible_h__
+
+#include "HTMLFormControlAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * Selects, Listboxes and Comboboxes, are made up of a number of different
+ * widgets, some of which are shared between the two. This file contains
+ * all of the widgets for both of the Selects, for HTML only.
+ *
+ * Listbox:
+ * - HTMLSelectListAccessible
+ * - HTMLSelectOptionAccessible
+ *
+ * Comboboxes:
+ * - HTMLComboboxAccessible
+ * - HTMLComboboxListAccessible [ inserted in accessible tree ]
+ * - HTMLSelectOptionAccessible(s)
+ */
+
+/*
+ * The list that contains all the options in the select.
+ */
+class HTMLSelectListAccessible : public AccessibleWrap
+{
+public:
+
+ HTMLSelectListAccessible(nsIContent* aContent, DocAccessible* aDoc);
+ virtual ~HTMLSelectListAccessible() {}
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+
+ // SelectAccessible
+ 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 bool IsAcceptableChild(nsIContent* aEl) const override;
+};
+
+/*
+ * Options inside the select, contained within the list
+ */
+class HTMLSelectOptionAccessible : public HyperTextAccessibleWrap
+{
+public:
+ enum { eAction_Select = 0 };
+
+ HTMLSelectOptionAccessible(nsIContent* aContent, DocAccessible* aDoc);
+ virtual ~HTMLSelectOptionAccessible() {}
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+ virtual uint64_t NativeInteractiveState() const override;
+
+ virtual int32_t GetLevelInternal() override;
+ virtual nsRect RelativeBounds(nsIFrame** aBoundingFrame) const override;
+ virtual void SetSelected(bool aSelect) 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;
+
+protected:
+ // Accessible
+ virtual ENameValueFlag NativeName(nsString& aName) override;
+
+private:
+
+ /**
+ * Return a select accessible the option belongs to if any.
+ */
+ Accessible* GetSelect() const
+ {
+ Accessible* parent = mParent;
+ if (parent && parent->IsHTMLOptGroup())
+ parent = parent->Parent();
+
+ if (parent && parent->IsListControl()) {
+ Accessible* combobox = parent->Parent();
+ return combobox && combobox->IsCombobox() ? combobox : mParent;
+ }
+
+ return nullptr;
+ }
+
+ /**
+ * Return a combobox accessible the option belongs to if any.
+ */
+ Accessible* GetCombobox() const
+ {
+ Accessible* parent = mParent;
+ if (parent && parent->IsHTMLOptGroup())
+ parent = parent->Parent();
+
+ if (parent && parent->IsListControl()) {
+ Accessible* combobox = parent->Parent();
+ return combobox && combobox->IsCombobox() ? combobox : nullptr;
+ }
+
+ return nullptr;
+ }
+};
+
+/*
+ * Opt Groups inside the select, contained within the list
+ */
+class HTMLSelectOptGroupAccessible : public HTMLSelectOptionAccessible
+{
+public:
+
+ HTMLSelectOptGroupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HTMLSelectOptionAccessible(aContent, aDoc)
+ { mType = eHTMLOptGroupType; }
+ virtual ~HTMLSelectOptGroupAccessible() {}
+
+ // Accessible
+ virtual a11y::role NativeRole() 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;
+};
+
+/** ------------------------------------------------------ */
+/** Finally, the Combobox widgets */
+/** ------------------------------------------------------ */
+
+class HTMLComboboxListAccessible;
+
+/*
+ * A class the represents the HTML Combobox widget.
+ */
+class HTMLComboboxAccessible final : public AccessibleWrap
+{
+public:
+ enum { eAction_Click = 0 };
+
+ HTMLComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc);
+ virtual ~HTMLComboboxAccessible() {}
+
+ // Accessible
+ virtual void Shutdown() override;
+ virtual void Description(nsString& aDescription) override;
+ virtual void Value(nsString& aValue) override;
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+ virtual bool RemoveChild(Accessible* aChild) 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* CurrentItem() override;
+ virtual void SetCurrentItem(Accessible* aItem) override;
+
+protected:
+ /**
+ * Return selected option.
+ */
+ Accessible* SelectedOption() const;
+
+private:
+ RefPtr<HTMLComboboxListAccessible> mListAccessible;
+};
+
+/*
+ * A class that represents the window that lives to the right
+ * of the drop down button inside the Select. This is the window
+ * that is made visible when the button is pressed.
+ */
+class HTMLComboboxListAccessible : public HTMLSelectListAccessible
+{
+public:
+
+ HTMLComboboxListAccessible(Accessible* aParent, nsIContent* aContent,
+ DocAccessible* aDoc);
+ virtual ~HTMLComboboxListAccessible() {}
+
+ // Accessible
+ virtual nsIFrame* GetFrame() const override;
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+ virtual nsRect RelativeBounds(nsIFrame** aBoundingFrame) const override;
+
+ // Widgets
+ virtual bool IsActiveWidget() const override;
+ virtual bool AreItemsOperable() const override;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/html/HTMLTableAccessible.cpp b/accessible/html/HTMLTableAccessible.cpp
new file mode 100644
index 0000000000..b0cdc0932d
--- /dev/null
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -0,0 +1,1139 @@
+/* -*- 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 "HTMLTableAccessible.h"
+
+#include "mozilla/DebugOnly.h"
+
+#include "Accessible-inl.h"
+#include "nsAccessibilityService.h"
+#include "nsAccUtils.h"
+#include "DocAccessible.h"
+#include "nsTextEquivUtils.h"
+#include "Relation.h"
+#include "Role.h"
+#include "States.h"
+#include "TreeWalker.h"
+
+#include "mozilla/dom/HTMLTableElement.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMRange.h"
+#include "nsISelectionPrivate.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMHTMLCollection.h"
+#include "nsIDocument.h"
+#include "nsIMutableArray.h"
+#include "nsIPersistentProperties2.h"
+#include "nsIPresShell.h"
+#include "nsITableCellLayout.h"
+#include "nsFrameSelection.h"
+#include "nsError.h"
+#include "nsArrayUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNameSpaceManager.h"
+#include "nsTableCellFrame.h"
+#include "nsTableWrapperFrame.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableCellAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLTableCellAccessible::
+ HTMLTableCellAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc)
+{
+ mType = eHTMLTableCellType;
+ mGenericTypes |= eTableCell;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableCellAccessible, HyperTextAccessible)
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableCellAccessible: Accessible implementation
+
+role
+HTMLTableCellAccessible::NativeRole()
+{
+ if (mContent->IsMathMLElement(nsGkAtoms::mtd_)) {
+ return roles::MATHML_CELL;
+ }
+ return roles::CELL;
+}
+
+uint64_t
+HTMLTableCellAccessible::NativeState()
+{
+ uint64_t state = HyperTextAccessibleWrap::NativeState();
+
+ nsIFrame *frame = mContent->GetPrimaryFrame();
+ NS_ASSERTION(frame, "No frame for valid cell accessible!");
+
+ if (frame && frame->IsSelected())
+ state |= states::SELECTED;
+
+ return state;
+}
+
+uint64_t
+HTMLTableCellAccessible::NativeInteractiveState() const
+{
+ return HyperTextAccessibleWrap::NativeInteractiveState() | states::SELECTABLE;
+}
+
+already_AddRefed<nsIPersistentProperties>
+HTMLTableCellAccessible::NativeAttributes()
+{
+ nsCOMPtr<nsIPersistentProperties> attributes =
+ HyperTextAccessibleWrap::NativeAttributes();
+
+ // table-cell-index attribute
+ TableAccessible* table = Table();
+ if (!table)
+ return attributes.forget();
+
+ int32_t rowIdx = -1, colIdx = -1;
+ nsresult rv = GetCellIndexes(rowIdx, colIdx);
+ if (NS_FAILED(rv))
+ return attributes.forget();
+
+ nsAutoString stringIdx;
+ stringIdx.AppendInt(table->CellIndexAt(rowIdx, colIdx));
+ nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx);
+
+ // abbr attribute
+
+ // Pick up object attribute from abbr DOM element (a child of the cell) or
+ // from abbr DOM attribute.
+ nsAutoString abbrText;
+ if (ChildCount() == 1) {
+ Accessible* abbr = FirstChild();
+ if (abbr->IsAbbreviation()) {
+ nsIContent* firstChildNode = abbr->GetContent()->GetFirstChild();
+ if (firstChildNode) {
+ nsTextEquivUtils::
+ AppendTextEquivFromTextContent(firstChildNode, &abbrText);
+ }
+ }
+ }
+ if (abbrText.IsEmpty())
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText);
+
+ if (!abbrText.IsEmpty())
+ nsAccUtils::SetAccAttr(attributes, nsGkAtoms::abbr, abbrText);
+
+ // axis attribute
+ nsAutoString axisText;
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText);
+ if (!axisText.IsEmpty())
+ nsAccUtils::SetAccAttr(attributes, nsGkAtoms::axis, axisText);
+
+#ifdef DEBUG
+ nsAutoString unused;
+ attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"),
+ NS_LITERAL_STRING("HTMLTableCellAccessible"),
+ unused);
+#endif
+
+ return attributes.forget();
+}
+
+GroupPos
+HTMLTableCellAccessible::GroupPosition()
+{
+ int32_t count = 0, index = 0;
+ TableAccessible* table = Table();
+ if (table && nsCoreUtils::GetUIntAttr(table->AsAccessible()->GetContent(),
+ nsGkAtoms::aria_colcount, &count) &&
+ nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_colindex, &index)) {
+ return GroupPos(0, index, count);
+ }
+
+ return HyperTextAccessibleWrap::GroupPosition();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableCellAccessible: TableCellAccessible implementation
+
+TableAccessible*
+HTMLTableCellAccessible::Table() const
+{
+ Accessible* parent = const_cast<HTMLTableCellAccessible*>(this);
+ while ((parent = parent->Parent())) {
+ if (parent->IsTable())
+ return parent->AsTable();
+ }
+
+ return nullptr;
+}
+
+uint32_t
+HTMLTableCellAccessible::ColIdx() const
+{
+ nsITableCellLayout* cellLayout = GetCellLayout();
+ NS_ENSURE_TRUE(cellLayout, 0);
+
+ int32_t colIdx = 0;
+ cellLayout->GetColIndex(colIdx);
+ return colIdx > 0 ? static_cast<uint32_t>(colIdx) : 0;
+}
+
+uint32_t
+HTMLTableCellAccessible::RowIdx() const
+{
+ nsITableCellLayout* cellLayout = GetCellLayout();
+ NS_ENSURE_TRUE(cellLayout, 0);
+
+ int32_t rowIdx = 0;
+ cellLayout->GetRowIndex(rowIdx);
+ return rowIdx > 0 ? static_cast<uint32_t>(rowIdx) : 0;
+}
+
+uint32_t
+HTMLTableCellAccessible::ColExtent() const
+{
+ int32_t rowIdx = -1, colIdx = -1;
+ GetCellIndexes(rowIdx, colIdx);
+
+ TableAccessible* table = Table();
+ NS_ASSERTION(table, "cell not in a table!");
+ if (!table)
+ return 0;
+
+ return table->ColExtentAt(rowIdx, colIdx);
+}
+
+uint32_t
+HTMLTableCellAccessible::RowExtent() const
+{
+ int32_t rowIdx = -1, colIdx = -1;
+ GetCellIndexes(rowIdx, colIdx);
+
+ TableAccessible* table = Table();
+ NS_ASSERTION(table, "cell not in atable!");
+ if (!table)
+ return 0;
+
+ return table->RowExtentAt(rowIdx, colIdx);
+}
+
+void
+HTMLTableCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells)
+{
+ IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers);
+ while (Accessible* cell = itr.Next()) {
+ a11y::role cellRole = cell->Role();
+ if (cellRole == roles::COLUMNHEADER) {
+ aCells->AppendElement(cell);
+ } else if (cellRole != roles::ROWHEADER) {
+ // If referred table cell is at the same column then treat it as a column
+ // header.
+ TableCellAccessible* tableCell = cell->AsTableCell();
+ if (tableCell && tableCell->ColIdx() == ColIdx())
+ aCells->AppendElement(cell);
+ }
+ }
+
+ if (aCells->IsEmpty())
+ TableCellAccessible::ColHeaderCells(aCells);
+}
+
+void
+HTMLTableCellAccessible::RowHeaderCells(nsTArray<Accessible*>* aCells)
+{
+ IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers);
+ while (Accessible* cell = itr.Next()) {
+ a11y::role cellRole = cell->Role();
+ if (cellRole == roles::ROWHEADER) {
+ aCells->AppendElement(cell);
+ } else if (cellRole != roles::COLUMNHEADER) {
+ // If referred table cell is at the same row then treat it as a column
+ // header.
+ TableCellAccessible* tableCell = cell->AsTableCell();
+ if (tableCell && tableCell->RowIdx() == RowIdx())
+ aCells->AppendElement(cell);
+ }
+ }
+
+ if (aCells->IsEmpty())
+ TableCellAccessible::RowHeaderCells(aCells);
+}
+
+bool
+HTMLTableCellAccessible::Selected()
+{
+ int32_t rowIdx = -1, colIdx = -1;
+ GetCellIndexes(rowIdx, colIdx);
+
+ TableAccessible* table = Table();
+ NS_ENSURE_TRUE(table, false);
+
+ return table->IsCellSelected(rowIdx, colIdx);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableCellAccessible: protected implementation
+
+nsITableCellLayout*
+HTMLTableCellAccessible::GetCellLayout() const
+{
+ return do_QueryFrame(mContent->GetPrimaryFrame());
+}
+
+nsresult
+HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const
+{
+ nsITableCellLayout *cellLayout = GetCellLayout();
+ NS_ENSURE_STATE(cellLayout);
+
+ return cellLayout->GetCellIndexes(aRowIdx, aColIdx);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableHeaderCellAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLTableHeaderCellAccessible::
+ HTMLTableHeaderCellAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HTMLTableCellAccessible(aContent, aDoc)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableHeaderCellAccessible: Accessible implementation
+
+role
+HTMLTableHeaderCellAccessible::NativeRole()
+{
+ // Check value of @scope attribute.
+ static nsIContent::AttrValuesArray scopeValues[] =
+ { &nsGkAtoms::col, &nsGkAtoms::colgroup,
+ &nsGkAtoms::row, &nsGkAtoms::rowgroup, nullptr };
+ int32_t valueIdx =
+ mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope,
+ scopeValues, eCaseMatters);
+
+ switch (valueIdx) {
+ case 0:
+ case 1:
+ return roles::COLUMNHEADER;
+ case 2:
+ case 3:
+ return roles::ROWHEADER;
+ }
+
+ TableAccessible* table = Table();
+ if (!table)
+ return roles::NOTHING;
+
+ // If the cell next to this one is not a header cell then assume this cell is
+ // a row header for it.
+ uint32_t rowIdx = RowIdx(), colIdx = ColIdx();
+ Accessible* cell = table->CellAt(rowIdx, colIdx + ColExtent());
+ if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent()))
+ return roles::ROWHEADER;
+
+ // If the cell below this one is not a header cell then assume this cell is
+ // a column header for it.
+ uint32_t rowExtent = RowExtent();
+ cell = table->CellAt(rowIdx + rowExtent, colIdx);
+ if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent()))
+ return roles::COLUMNHEADER;
+
+ // Otherwise if this cell is surrounded by header cells only then make a guess
+ // based on its cell spanning. In other words if it is row spanned then assume
+ // it's a row header, otherwise it's a column header.
+ return rowExtent > 1 ? roles::ROWHEADER : roles::COLUMNHEADER;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableRowAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableRowAccessible, Accessible)
+
+role
+HTMLTableRowAccessible::NativeRole()
+{
+ if (mContent->IsMathMLElement(nsGkAtoms::mtr_)) {
+ return roles::MATHML_TABLE_ROW;
+ } else if (mContent->IsMathMLElement(nsGkAtoms::mlabeledtr_)) {
+ return roles::MATHML_LABELED_ROW;
+ }
+ return roles::ROW;
+}
+
+GroupPos
+HTMLTableRowAccessible::GroupPosition()
+{
+ int32_t count = 0, index = 0;
+ Accessible* table = nsAccUtils::TableFor(this);
+ if (table && nsCoreUtils::GetUIntAttr(table->GetContent(),
+ nsGkAtoms::aria_rowcount, &count) &&
+ nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_rowindex, &index)) {
+ return GroupPos(0, index, count);
+ }
+
+ return AccessibleWrap::GroupPosition();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableAccessible, Accessible)
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableAccessible: Accessible
+
+bool
+HTMLTableAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
+{
+ // Move caption accessible so that it's the first child. Check for the first
+ // caption only, because nsAccessibilityService ensures we don't create
+ // accessibles for the other captions, since only the first is actually
+ // visible.
+ return Accessible::InsertChildAt(aChild->IsHTMLCaption() ? 0 : aIndex, aChild);
+}
+
+role
+HTMLTableAccessible::NativeRole()
+{
+ if (mContent->IsMathMLElement(nsGkAtoms::mtable_)) {
+ return roles::MATHML_TABLE;
+ }
+ return roles::TABLE;
+}
+
+uint64_t
+HTMLTableAccessible::NativeState()
+{
+ return Accessible::NativeState() | states::READONLY;
+}
+
+ENameValueFlag
+HTMLTableAccessible::NativeName(nsString& aName)
+{
+ ENameValueFlag nameFlag = Accessible::NativeName(aName);
+ if (!aName.IsEmpty())
+ return nameFlag;
+
+ // Use table caption as a name.
+ Accessible* caption = Caption();
+ if (caption) {
+ nsIContent* captionContent = caption->GetContent();
+ if (captionContent) {
+ nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName);
+ if (!aName.IsEmpty())
+ return eNameOK;
+ }
+ }
+
+ // If no caption then use summary as a name.
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName);
+ return eNameOK;
+}
+
+already_AddRefed<nsIPersistentProperties>
+HTMLTableAccessible::NativeAttributes()
+{
+ nsCOMPtr<nsIPersistentProperties> attributes =
+ AccessibleWrap::NativeAttributes();
+
+ if (mContent->IsMathMLElement(nsGkAtoms::mtable_)) {
+ GetAccService()->MarkupAttributes(mContent, attributes);
+ }
+
+ if (IsProbablyLayoutTable()) {
+ nsAutoString unused;
+ attributes->SetStringProperty(NS_LITERAL_CSTRING("layout-guess"),
+ NS_LITERAL_STRING("true"), unused);
+ }
+
+ return attributes.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableAccessible: Accessible
+
+Relation
+HTMLTableAccessible::RelationByType(RelationType aType)
+{
+ Relation rel = AccessibleWrap::RelationByType(aType);
+ if (aType == RelationType::LABELLED_BY)
+ rel.AppendTarget(Caption());
+
+ return rel;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLTableAccessible: Table
+
+Accessible*
+HTMLTableAccessible::Caption() const
+{
+ Accessible* child = mChildren.SafeElementAt(0, nullptr);
+ return child && child->Role() == roles::CAPTION ? child : nullptr;
+}
+
+void
+HTMLTableAccessible::Summary(nsString& aSummary)
+{
+ dom::HTMLTableElement* table = dom::HTMLTableElement::FromContent(mContent);
+
+ if (table)
+ table->GetSummary(aSummary);
+}
+
+uint32_t
+HTMLTableAccessible::ColCount()
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ return tableFrame ? tableFrame->GetColCount() : 0;
+}
+
+uint32_t
+HTMLTableAccessible::RowCount()
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ return tableFrame ? tableFrame->GetRowCount() : 0;
+}
+
+uint32_t
+HTMLTableAccessible::SelectedCellCount()
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return 0;
+
+ uint32_t count = 0, rowCount = RowCount(), colCount = ColCount();
+ for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
+ nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
+ if (!cellFrame || !cellFrame->IsSelected())
+ continue;
+
+ int32_t startRow = -1, startCol = -1;
+ cellFrame->GetRowIndex(startRow);
+ cellFrame->GetColIndex(startCol);
+ if (startRow >= 0 && (uint32_t)startRow == rowIdx &&
+ startCol >= 0 && (uint32_t)startCol == colIdx)
+ count++;
+ }
+ }
+
+ return count;
+}
+
+uint32_t
+HTMLTableAccessible::SelectedColCount()
+{
+ uint32_t count = 0, colCount = ColCount();
+
+ for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
+ if (IsColSelected(colIdx))
+ count++;
+
+ return count;
+}
+
+uint32_t
+HTMLTableAccessible::SelectedRowCount()
+{
+ uint32_t count = 0, rowCount = RowCount();
+
+ for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++)
+ if (IsRowSelected(rowIdx))
+ count++;
+
+ return count;
+}
+
+void
+HTMLTableAccessible::SelectedCells(nsTArray<Accessible*>* aCells)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return;
+
+ uint32_t rowCount = RowCount(), colCount = ColCount();
+ for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
+ nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
+ if (!cellFrame || !cellFrame->IsSelected())
+ continue;
+
+ int32_t startCol = -1, startRow = -1;
+ cellFrame->GetRowIndex(startRow);
+ cellFrame->GetColIndex(startCol);
+ if ((startRow >= 0 && (uint32_t)startRow != rowIdx) ||
+ (startCol >= 0 && (uint32_t)startCol != colIdx))
+ continue;
+
+ Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent());
+ aCells->AppendElement(cell);
+ }
+ }
+}
+
+void
+HTMLTableAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return;
+
+ uint32_t rowCount = RowCount(), colCount = ColCount();
+ for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
+ nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
+ if (!cellFrame || !cellFrame->IsSelected())
+ continue;
+
+ int32_t startRow = -1, startCol = -1;
+ cellFrame->GetColIndex(startCol);
+ cellFrame->GetRowIndex(startRow);
+ if (startRow >= 0 && (uint32_t)startRow == rowIdx &&
+ startCol >= 0 && (uint32_t)startCol == colIdx)
+ aCells->AppendElement(CellIndexAt(rowIdx, colIdx));
+ }
+ }
+}
+
+void
+HTMLTableAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
+{
+ uint32_t colCount = ColCount();
+ for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
+ if (IsColSelected(colIdx))
+ aCols->AppendElement(colIdx);
+}
+
+void
+HTMLTableAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows)
+{
+ uint32_t rowCount = RowCount();
+ for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++)
+ if (IsRowSelected(rowIdx))
+ aRows->AppendElement(rowIdx);
+}
+
+Accessible*
+HTMLTableAccessible::CellAt(uint32_t aRowIdx, uint32_t aColIdx)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return nullptr;
+
+ nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx);
+ Accessible* cell = mDoc->GetAccessible(cellContent);
+
+ // XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may
+ // return itself as a cell what makes Orca hang.
+ return cell == this ? nullptr : cell;
+}
+
+int32_t
+HTMLTableAccessible::CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return -1;
+
+ return tableFrame->GetIndexByRowAndColumn(aRowIdx, aColIdx);
+}
+
+int32_t
+HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return -1;
+
+ int32_t rowIdx = -1, colIdx = -1;
+ tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
+ return colIdx;
+}
+
+int32_t
+HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return -1;
+
+ int32_t rowIdx = -1, colIdx = -1;
+ tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
+ return rowIdx;
+}
+
+void
+HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx,
+ int32_t* aColIdx)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (tableFrame)
+ tableFrame->GetRowAndColumnByIndex(aCellIdx, aRowIdx, aColIdx);
+}
+
+uint32_t
+HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return 0;
+
+ return tableFrame->GetEffectiveColSpanAt(aRowIdx, aColIdx);
+}
+
+uint32_t
+HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return 0;
+
+ return tableFrame->GetEffectiveRowSpanAt(aRowIdx, aColIdx);
+}
+
+bool
+HTMLTableAccessible::IsColSelected(uint32_t aColIdx)
+{
+ bool isSelected = false;
+
+ uint32_t rowCount = RowCount();
+ for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ isSelected = IsCellSelected(rowIdx, aColIdx);
+ if (!isSelected)
+ return false;
+ }
+
+ return isSelected;
+}
+
+bool
+HTMLTableAccessible::IsRowSelected(uint32_t aRowIdx)
+{
+ bool isSelected = false;
+
+ uint32_t colCount = ColCount();
+ for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
+ isSelected = IsCellSelected(aRowIdx, colIdx);
+ if (!isSelected)
+ return false;
+ }
+
+ return isSelected;
+}
+
+bool
+HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return false;
+
+ nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(aRowIdx, aColIdx);
+ return cellFrame ? cellFrame->IsSelected() : false;
+}
+
+void
+HTMLTableAccessible::SelectRow(uint32_t aRowIdx)
+{
+ DebugOnly<nsresult> rv =
+ RemoveRowsOrColumnsFromSelection(aRowIdx,
+ nsISelectionPrivate::TABLESELECTION_ROW,
+ true);
+ NS_ASSERTION(NS_SUCCEEDED(rv),
+ "RemoveRowsOrColumnsFromSelection() Shouldn't fail!");
+
+ AddRowOrColumnToSelection(aRowIdx, nsISelectionPrivate::TABLESELECTION_ROW);
+}
+
+void
+HTMLTableAccessible::SelectCol(uint32_t aColIdx)
+{
+ DebugOnly<nsresult> rv =
+ RemoveRowsOrColumnsFromSelection(aColIdx,
+ nsISelectionPrivate::TABLESELECTION_COLUMN,
+ true);
+ NS_ASSERTION(NS_SUCCEEDED(rv),
+ "RemoveRowsOrColumnsFromSelection() Shouldn't fail!");
+
+ AddRowOrColumnToSelection(aColIdx, nsISelectionPrivate::TABLESELECTION_COLUMN);
+}
+
+void
+HTMLTableAccessible::UnselectRow(uint32_t aRowIdx)
+{
+ RemoveRowsOrColumnsFromSelection(aRowIdx,
+ nsISelectionPrivate::TABLESELECTION_ROW,
+ false);
+}
+
+void
+HTMLTableAccessible::UnselectCol(uint32_t aColIdx)
+{
+ RemoveRowsOrColumnsFromSelection(aColIdx,
+ nsISelectionPrivate::TABLESELECTION_COLUMN,
+ false);
+}
+
+nsresult
+HTMLTableAccessible::AddRowOrColumnToSelection(int32_t aIndex, uint32_t aTarget)
+{
+ bool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
+
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return NS_OK;
+
+ uint32_t count = 0;
+ if (doSelectRow)
+ count = ColCount();
+ else
+ count = RowCount();
+
+ nsIPresShell* presShell(mDoc->PresShell());
+ RefPtr<nsFrameSelection> tableSelection =
+ const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
+
+ for (uint32_t idx = 0; idx < count; idx++) {
+ int32_t rowIdx = doSelectRow ? aIndex : idx;
+ int32_t colIdx = doSelectRow ? idx : aIndex;
+ nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
+ if (cellFrame && !cellFrame->IsSelected()) {
+ nsresult rv = tableSelection->SelectCellElement(cellFrame->GetContent());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+HTMLTableAccessible::RemoveRowsOrColumnsFromSelection(int32_t aIndex,
+ uint32_t aTarget,
+ bool aIsOuter)
+{
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ return NS_OK;
+
+ nsIPresShell* presShell(mDoc->PresShell());
+ RefPtr<nsFrameSelection> tableSelection =
+ const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
+
+ bool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
+ uint32_t count = doUnselectRow ? ColCount() : RowCount();
+
+ int32_t startRowIdx = doUnselectRow ? aIndex : 0;
+ int32_t endRowIdx = doUnselectRow ? aIndex : count - 1;
+ int32_t startColIdx = doUnselectRow ? 0 : aIndex;
+ int32_t endColIdx = doUnselectRow ? count - 1 : aIndex;
+
+ if (aIsOuter)
+ return tableSelection->RestrictCellsToSelection(mContent,
+ startRowIdx, startColIdx,
+ endRowIdx, endColIdx);
+
+ return tableSelection->RemoveCellsFromSelection(mContent,
+ startRowIdx, startColIdx,
+ endRowIdx, endColIdx);
+}
+
+void
+HTMLTableAccessible::Description(nsString& aDescription)
+{
+ // Helpful for debugging layout vs. data tables
+ aDescription.Truncate();
+ Accessible::Description(aDescription);
+ if (!aDescription.IsEmpty())
+ return;
+
+ // Use summary as description if it weren't used as a name.
+ // XXX: get rid code duplication with NameInternal().
+ Accessible* caption = Caption();
+ if (caption) {
+ nsIContent* captionContent = caption->GetContent();
+ if (captionContent) {
+ nsAutoString captionText;
+ nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent,
+ &captionText);
+
+ if (!captionText.IsEmpty()) { // summary isn't used as a name.
+ mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary,
+ aDescription);
+ }
+ }
+ }
+
+#ifdef SHOW_LAYOUT_HEURISTIC
+ if (aDescription.IsEmpty()) {
+ bool isProbablyForLayout = IsProbablyLayoutTable();
+ aDescription = mLayoutHeuristic;
+ }
+ printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic).get());
+#endif
+}
+
+bool
+HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty)
+{
+ nsCOMPtr<nsIHTMLCollection> elements =
+ mContent->AsElement()->GetElementsByTagName(aTagName);
+
+ Element* foundItem = elements->Item(0);
+ if (!foundItem)
+ return false;
+
+ if (aAllowEmpty)
+ return true;
+
+ // Make sure that the item we found has contents and either has multiple
+ // children or the found item is not a whitespace-only text node.
+ if (foundItem->GetChildCount() > 1)
+ return true; // Treat multiple child nodes as non-empty
+
+ nsIContent *innerItemContent = foundItem->GetFirstChild();
+ if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace())
+ return true;
+
+ // If we found more than one node then return true not depending on
+ // aAllowEmpty flag.
+ // XXX it might be dummy but bug 501375 where we changed this addresses
+ // performance problems only. Note, currently 'aAllowEmpty' flag is used for
+ // caption element only. On another hand we create accessible object for
+ // the first entry of caption element (see
+ // HTMLTableAccessible::InsertChildAt).
+ return !!elements->Item(1);
+}
+
+bool
+HTMLTableAccessible::IsProbablyLayoutTable()
+{
+ // Implement a heuristic to determine if table is most likely used for layout
+ // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells
+ // at the beginning or end of a row/col, and especially when they occur at the edge of a table?
+ // XXX expose this info via object attributes to AT-SPI
+
+ // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC
+ // This will allow release trunk builds to be used by testers to refine the algorithm
+ // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release
+#ifdef SHOW_LAYOUT_HEURISTIC
+#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
+ { \
+ mLayoutHeuristic = isLayout ? \
+ NS_LITERAL_STRING("layout table: " heuristic) : \
+ NS_LITERAL_STRING("data table: " heuristic); \
+ return isLayout; \
+ }
+#else
+#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; }
+#endif
+
+ DocAccessible* docAccessible = Document();
+ if (docAccessible) {
+ uint64_t docState = docAccessible->State();
+ if (docState & states::EDITABLE) { // Need to see all elements while document is being edited
+ RETURN_LAYOUT_ANSWER(false, "In editable document");
+ }
+ }
+
+ // Check to see if an ARIA role overrides the role from native markup,
+ // but for which we still expose table semantics (treegrid, for example).
+ if (Role() != roles::TABLE)
+ RETURN_LAYOUT_ANSWER(false, "Has role attribute");
+
+ if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
+ // Role attribute is present, but overridden roles have already been dealt with.
+ // Only landmarks and other roles that don't override the role from native
+ // markup are left to deal with here.
+ RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
+ }
+
+ NS_ASSERTION(mContent->IsHTMLElement(nsGkAtoms::table),
+ "table should not be built by CSS display:table style");
+
+ // Check if datatable attribute has "0" value.
+ if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
+ NS_LITERAL_STRING("0"), eCaseMatters)) {
+ RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
+ }
+
+ // Check for legitimate data table attributes.
+ nsAutoString summary;
+ if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
+ !summary.IsEmpty())
+ RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
+
+ // Check for legitimate data table elements.
+ Accessible* caption = FirstChild();
+ if (caption && caption->Role() == roles::CAPTION && caption->HasChildren())
+ RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures");
+
+ for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
+ childElm = childElm->GetNextSibling()) {
+ if (!childElm->IsHTMLElement())
+ continue;
+
+ if (childElm->IsAnyOfHTMLElements(nsGkAtoms::col,
+ nsGkAtoms::colgroup,
+ nsGkAtoms::tfoot,
+ nsGkAtoms::thead)) {
+ RETURN_LAYOUT_ANSWER(false,
+ "Has col, colgroup, tfoot or thead -- legitimate table structures");
+ }
+
+ if (childElm->IsHTMLElement(nsGkAtoms::tbody)) {
+ for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
+ rowElm = rowElm->GetNextSibling()) {
+ if (rowElm->IsHTMLElement(nsGkAtoms::tr)) {
+ for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
+ cellElm = cellElm->GetNextSibling()) {
+ if (cellElm->IsHTMLElement()) {
+
+ if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
+ RETURN_LAYOUT_ANSWER(false,
+ "Has th -- legitimate table structures");
+ }
+
+ if (cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
+ cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
+ cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
+ RETURN_LAYOUT_ANSWER(false,
+ "Has headers, scope, or abbr attribute -- legitimate table structures");
+ }
+
+ Accessible* cell = mDoc->GetAccessible(cellElm);
+ if (cell && cell->ChildCount() == 1 &&
+ cell->FirstChild()->IsAbbreviation()) {
+ RETURN_LAYOUT_ANSWER(false,
+ "has abbr -- legitimate table structures");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (HasDescendant(NS_LITERAL_STRING("table"))) {
+ RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
+ }
+
+ // If only 1 column or only 1 row, it's for layout
+ uint32_t colCount = ColCount();
+ if (colCount <=1) {
+ RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
+ }
+ uint32_t rowCount = RowCount();
+ if (rowCount <=1) {
+ RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
+ }
+
+ // Check for many columns
+ if (colCount >= 5) {
+ RETURN_LAYOUT_ANSWER(false, ">=5 columns");
+ }
+
+ // Now we know there are 2-4 columns and 2 or more rows
+ // Check to see if there are visible borders on the cells
+ // XXX currently, we just check the first cell -- do we really need to do more?
+ nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+ if (!tableFrame)
+ RETURN_LAYOUT_ANSWER(false, "table with no frame!");
+
+ nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0);
+ if (!cellFrame)
+ RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
+
+ nsMargin border;
+ cellFrame->GetXULBorder(border);
+ if (border.top && border.bottom && border.left && border.right) {
+ RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
+ }
+
+ /**
+ * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
+ */
+
+ // Check for styled background color across rows (alternating background
+ // color is a common feature for data tables).
+ uint32_t childCount = ChildCount();
+ nscolor rowColor = 0;
+ nscolor prevRowColor;
+ for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
+ Accessible* child = GetChildAt(childIdx);
+ if (child->Role() == roles::ROW) {
+ prevRowColor = rowColor;
+ nsIFrame* rowFrame = child->GetFrame();
+ rowColor = rowFrame->StyleBackground()->mBackgroundColor;
+
+ if (childIdx > 0 && prevRowColor != rowColor)
+ RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered");
+ }
+ }
+
+ // Check for many rows
+ const uint32_t kMaxLayoutRows = 20;
+ if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data
+ RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
+ }
+
+ // Check for very wide table.
+ nsIFrame* documentFrame = Document()->GetFrame();
+ nsSize documentSize = documentFrame->GetSize();
+ if (documentSize.width > 0) {
+ nsSize tableSize = GetFrame()->GetSize();
+ int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width;
+ if (percentageOfDocWidth > 95) {
+ // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
+ // Probably for layout
+ RETURN_LAYOUT_ANSWER(true,
+ "<= 4 columns, table width is 95% of document width");
+ }
+ }
+
+ // Two column rules
+ if (rowCount * colCount <= 10) {
+ RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
+ }
+
+ if (HasDescendant(NS_LITERAL_STRING("embed")) ||
+ HasDescendant(NS_LITERAL_STRING("object")) ||
+ HasDescendant(NS_LITERAL_STRING("applet")) ||
+ HasDescendant(NS_LITERAL_STRING("iframe"))) {
+ RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
+ }
+
+ RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data");
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLCaptionAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+Relation
+HTMLCaptionAccessible::RelationByType(RelationType aType)
+{
+ Relation rel = HyperTextAccessible::RelationByType(aType);
+ if (aType == RelationType::LABEL_FOR)
+ rel.AppendTarget(Parent());
+
+ return rel;
+}
+
+role
+HTMLCaptionAccessible::NativeRole()
+{
+ return roles::CAPTION;
+}
diff --git a/accessible/html/HTMLTableAccessible.h b/accessible/html/HTMLTableAccessible.h
new file mode 100644
index 0000000000..830d34ae9d
--- /dev/null
+++ b/accessible/html/HTMLTableAccessible.h
@@ -0,0 +1,226 @@
+/* -*- 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_HTMLTableAccessible_h__
+#define mozilla_a11y_HTMLTableAccessible_h__
+
+#include "HyperTextAccessibleWrap.h"
+#include "TableAccessible.h"
+#include "TableCellAccessible.h"
+
+class nsITableCellLayout;
+
+namespace mozilla {
+namespace a11y {
+
+/**
+ * HTML table cell accessible (html:td).
+ */
+class HTMLTableCellAccessible : public HyperTextAccessibleWrap,
+ public TableCellAccessible
+{
+public:
+ HTMLTableCellAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // nsISupports
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ virtual TableCellAccessible* AsTableCell() override { return this; }
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+ virtual uint64_t NativeInteractiveState() const override;
+ virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
+ virtual mozilla::a11y::GroupPos GroupPosition() override;
+
+ // TableCellAccessible
+ virtual TableAccessible* Table() const override;
+ virtual uint32_t ColIdx() const override;
+ virtual uint32_t RowIdx() const override;
+ virtual uint32_t ColExtent() const override;
+ virtual uint32_t RowExtent() const override;
+ virtual void ColHeaderCells(nsTArray<Accessible*>* aCells) override;
+ virtual void RowHeaderCells(nsTArray<Accessible*>* aCells) override;
+ virtual bool Selected() override;
+
+protected:
+ virtual ~HTMLTableCellAccessible() {}
+
+ /**
+ * Return nsITableCellLayout of the table cell frame.
+ */
+ nsITableCellLayout* GetCellLayout() const;
+
+ /**
+ * Return row and column indices of the cell.
+ */
+ nsresult GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const;
+};
+
+
+/**
+ * HTML table row/column header accessible (html:th or html:td@scope).
+ */
+class HTMLTableHeaderCellAccessible : public HTMLTableCellAccessible
+{
+public:
+ HTMLTableHeaderCellAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+};
+
+
+/**
+ * HTML table row accessible (html:tr).
+ */
+class HTMLTableRowAccessible : public AccessibleWrap
+{
+public:
+ HTMLTableRowAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ AccessibleWrap(aContent, aDoc)
+ {
+ mType = eHTMLTableRowType;
+ mGenericTypes |= eTableRow;
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+ virtual mozilla::a11y::GroupPos GroupPosition() override;
+
+protected:
+ virtual ~HTMLTableRowAccessible() { }
+};
+
+
+/**
+ * HTML table accessible (html:table).
+ */
+
+// To turn on table debugging descriptions define SHOW_LAYOUT_HEURISTIC
+// This allow release trunk builds to be used by testers to refine the
+// data vs. layout heuristic
+// #define SHOW_LAYOUT_HEURISTIC
+
+class HTMLTableAccessible : public AccessibleWrap,
+ public TableAccessible
+{
+public:
+ HTMLTableAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ AccessibleWrap(aContent, aDoc)
+ {
+ mType = eHTMLTableType;
+ mGenericTypes |= eTable;
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // TableAccessible
+ virtual Accessible* Caption() const override;
+ virtual void Summary(nsString& aSummary) override;
+ virtual uint32_t ColCount() override;
+ virtual uint32_t RowCount() override;
+ virtual Accessible* CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) override;
+ virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) override;
+ virtual int32_t ColIndexAt(uint32_t aCellIdx) override;
+ virtual int32_t RowIndexAt(uint32_t aCellIdx) override;
+ virtual void RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx,
+ int32_t* aColIdx) override;
+ virtual uint32_t ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) override;
+ virtual uint32_t RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) 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 SelectCol(uint32_t aColIdx) override;
+ virtual void SelectRow(uint32_t aRowIdx) override;
+ virtual void UnselectCol(uint32_t aColIdx) override;
+ virtual void UnselectRow(uint32_t aRowIdx) override;
+ virtual bool IsProbablyLayoutTable() override;
+ virtual Accessible* AsAccessible() override { return this; }
+
+ // Accessible
+ virtual TableAccessible* AsTable() override { return this; }
+ virtual void Description(nsString& aDescription) override;
+ virtual a11y::role NativeRole() override;
+ virtual uint64_t NativeState() override;
+ virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
+ virtual Relation RelationByType(RelationType aRelationType) override;
+
+ virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override;
+
+protected:
+ virtual ~HTMLTableAccessible() {}
+
+ // Accessible
+ virtual ENameValueFlag NativeName(nsString& aName) override;
+
+ // HTMLTableAccessible
+
+ /**
+ * Add row or column to selection.
+ *
+ * @param aIndex [in] index of row or column to be selected
+ * @param aTarget [in] indicates what should be selected, either row or column
+ * (see nsISelectionPrivate)
+ */
+ nsresult AddRowOrColumnToSelection(int32_t aIndex, uint32_t aTarget);
+
+ /**
+ * Removes rows or columns at the given index or outside it from selection.
+ *
+ * @param aIndex [in] row or column index
+ * @param aTarget [in] indicates whether row or column should unselected
+ * @param aIsOuter [in] indicates whether all rows or column excepting
+ * the given one should be unselected or the given one
+ * should be unselected only
+ */
+ nsresult RemoveRowsOrColumnsFromSelection(int32_t aIndex,
+ uint32_t aTarget,
+ bool aIsOuter);
+
+ /**
+ * Return true if table has an element with the given tag name.
+ *
+ * @param aTagName [in] tag name of searched element
+ * @param aAllowEmpty [in, optional] points if found element can be empty
+ * or contain whitespace text only.
+ */
+ bool HasDescendant(const nsAString& aTagName, bool aAllowEmpty = true);
+
+#ifdef SHOW_LAYOUT_HEURISTIC
+ nsString mLayoutHeuristic;
+#endif
+};
+
+/**
+ * HTML caption accessible (html:caption).
+ */
+class HTMLCaptionAccessible : public HyperTextAccessibleWrap
+{
+public:
+ HTMLCaptionAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+ HyperTextAccessibleWrap(aContent, aDoc) { mType = eHTMLCaptionType; }
+
+ // Accessible
+ virtual a11y::role NativeRole() override;
+ virtual Relation RelationByType(RelationType aRelationType) override;
+
+protected:
+ virtual ~HTMLCaptionAccessible() { }
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/html/moz.build b/accessible/html/moz.build
new file mode 100644
index 0000000000..8d92332cde
--- /dev/null
+++ b/accessible/html/moz.build
@@ -0,0 +1,50 @@
+# -*- 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 += [
+ 'HTMLCanvasAccessible.cpp',
+ 'HTMLElementAccessibles.cpp',
+ 'HTMLFormControlAccessible.cpp',
+ 'HTMLImageMapAccessible.cpp',
+ 'HTMLLinkAccessible.cpp',
+ 'HTMLListAccessible.cpp',
+ 'HTMLSelectAccessible.cpp',
+ 'HTMLTableAccessible.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/accessible/base',
+ '/accessible/generic',
+ '/accessible/xpcom',
+ '/layout/generic',
+ '/layout/tables',
+ '/layout/xul',
+]
+
+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']