From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- accessible/html/HTMLCanvasAccessible.cpp | 16 + accessible/html/HTMLCanvasAccessible.h | 35 + accessible/html/HTMLElementAccessibles.cpp | 231 ++++++ accessible/html/HTMLElementAccessibles.h | 159 +++++ accessible/html/HTMLFormControlAccessible.cpp | 979 ++++++++++++++++++++++++++ accessible/html/HTMLFormControlAccessible.h | 387 ++++++++++ accessible/html/HTMLImageMapAccessible.cpp | 200 ++++++ accessible/html/HTMLImageMapAccessible.h | 82 +++ accessible/html/HTMLLinkAccessible.cpp | 129 ++++ accessible/html/HTMLLinkAccessible.h | 59 ++ accessible/html/HTMLListAccessible.cpp | 112 +++ accessible/html/HTMLListAccessible.h | 85 +++ accessible/html/HTMLSelectAccessible.cpp | 472 +++++++++++++ accessible/html/HTMLSelectAccessible.h | 216 ++++++ accessible/html/HTMLTableAccessible.cpp | 712 +++++++++++++++++++ accessible/html/HTMLTableAccessible.h | 177 +++++ accessible/html/moz.build | 52 ++ 17 files changed, 4103 insertions(+) create mode 100644 accessible/html/HTMLCanvasAccessible.cpp create mode 100644 accessible/html/HTMLCanvasAccessible.h create mode 100644 accessible/html/HTMLElementAccessibles.cpp create mode 100644 accessible/html/HTMLElementAccessibles.h create mode 100644 accessible/html/HTMLFormControlAccessible.cpp create mode 100644 accessible/html/HTMLFormControlAccessible.h create mode 100644 accessible/html/HTMLImageMapAccessible.cpp create mode 100644 accessible/html/HTMLImageMapAccessible.h create mode 100644 accessible/html/HTMLLinkAccessible.cpp create mode 100644 accessible/html/HTMLLinkAccessible.h create mode 100644 accessible/html/HTMLListAccessible.cpp create mode 100644 accessible/html/HTMLListAccessible.h create mode 100644 accessible/html/HTMLSelectAccessible.cpp create mode 100644 accessible/html/HTMLSelectAccessible.h create mode 100644 accessible/html/HTMLTableAccessible.cpp create mode 100644 accessible/html/HTMLTableAccessible.h create mode 100644 accessible/html/moz.build (limited to 'accessible/html') diff --git a/accessible/html/HTMLCanvasAccessible.cpp b/accessible/html/HTMLCanvasAccessible.cpp new file mode 100644 index 0000000000..d6b02ce61c --- /dev/null +++ b/accessible/html/HTMLCanvasAccessible.cpp @@ -0,0 +1,16 @@ +/* -*- 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 "mozilla/a11y/Role.h" + +using namespace mozilla::a11y; + +HTMLCanvasAccessible::HTMLCanvasAccessible(nsIContent* aContent, + DocAccessible* aDoc) + : HyperTextAccessible(aContent, aDoc) {} + +role HTMLCanvasAccessible::NativeRole() const { return roles::CANVAS; } diff --git a/accessible/html/HTMLCanvasAccessible.h b/accessible/html/HTMLCanvasAccessible.h new file mode 100644 index 0000000000..49d443bce1 --- /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 "HyperTextAccessible.h" + +namespace mozilla { +namespace a11y { + +/** + * HTML canvas accessible (html:canvas). + */ +class HTMLCanvasAccessible : public HyperTextAccessible { + public: + HTMLCanvasAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // nsISupports + NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLCanvasAccessible, + HyperTextAccessible) + + // LocalAccessible + virtual a11y::role NativeRole() const 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..e01de31ff2 --- /dev/null +++ b/accessible/html/HTMLElementAccessibles.cpp @@ -0,0 +1,231 @@ +/* -*- 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 "CacheConstants.h" +#include "nsCoreUtils.h" +#include "nsTextEquivUtils.h" +#include "Relation.h" +#include "mozilla/a11y/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() const { return roles::SEPARATOR; } + +//////////////////////////////////////////////////////////////////////////////// +// HTMLBRAccessible +//////////////////////////////////////////////////////////////////////////////// + +role HTMLBRAccessible::NativeRole() const { return roles::WHITESPACE; } + +uint64_t HTMLBRAccessible::NativeState() const { return states::READONLY; } + +ENameValueFlag HTMLBRAccessible::NativeName(nsString& aName) const { + aName = static_cast('\n'); // Newline char + return eNameOK; +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLLabelAccessible +//////////////////////////////////////////////////////////////////////////////// + +ENameValueFlag HTMLLabelAccessible::NativeName(nsString& aName) const { + nsTextEquivUtils::GetNameFromSubtree(this, aName); + return aName.IsEmpty() ? eNameOK : eNameFromSubtree; +} + +Relation HTMLLabelAccessible::RelationByType(RelationType aType) const { + Relation rel = AccessibleWrap::RelationByType(aType); + if (aType == RelationType::LABEL_FOR) { + dom::HTMLLabelElement* label = dom::HTMLLabelElement::FromNode(mContent); + rel.AppendTarget(mDoc, label->GetControl()); + } + + return rel; +} + +void HTMLLabelAccessible::DOMAttributeChanged(int32_t aNameSpaceID, + nsAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue, + uint64_t aOldState) { + HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType, + aOldValue, aOldState); + + if (aAttribute == nsGkAtoms::_for) { + mDoc->QueueCacheUpdate(this, CacheDomain::Relations | CacheDomain::Actions); + } +} + +bool HTMLLabelAccessible::HasPrimaryAction() const { + return nsCoreUtils::IsLabelWithControl(mContent); +} + +void HTMLLabelAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) { + if (aIndex == 0) { + if (HasPrimaryAction()) { + aName.AssignLiteral("click"); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// nsHTMLOuputAccessible +//////////////////////////////////////////////////////////////////////////////// + +Relation HTMLOutputAccessible::RelationByType(RelationType aType) const { + Relation rel = AccessibleWrap::RelationByType(aType); + if (aType == RelationType::CONTROLLED_BY) { + rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::_for)); + } + + return rel; +} + +void HTMLOutputAccessible::DOMAttributeChanged(int32_t aNameSpaceID, + nsAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue, + uint64_t aOldState) { + HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType, + aOldValue, aOldState); + + if (aAttribute == nsGkAtoms::_for) { + mDoc->QueueCacheUpdate(this, CacheDomain::Relations); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLSummaryAccessible +//////////////////////////////////////////////////////////////////////////////// + +HTMLSummaryAccessible::HTMLSummaryAccessible(nsIContent* aContent, + DocAccessible* aDoc) + : HyperTextAccessible(aContent, aDoc) { + mGenericTypes |= eButton; +} + +bool HTMLSummaryAccessible::HasPrimaryAction() const { return true; } + +void HTMLSummaryAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) { + if (aIndex != eAction_Click) { + return; + } + + dom::HTMLSummaryElement* summary = + dom::HTMLSummaryElement::FromNode(mContent); + if (!summary) { + return; + } + + dom::HTMLDetailsElement* details = summary->GetDetails(); + if (!details) { + return; + } + + if (details->Open()) { + aName.AssignLiteral("collapse"); + } else { + aName.AssignLiteral("expand"); + } +} + +uint64_t HTMLSummaryAccessible::NativeState() const { + uint64_t state = HyperTextAccessible::NativeState(); + + dom::HTMLSummaryElement* summary = + dom::HTMLSummaryElement::FromNode(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* HTMLSummaryAccessible::FromDetails( + LocalAccessible* details) { + if (!dom::HTMLDetailsElement::FromNodeOrNull(details->GetContent())) { + return nullptr; + } + + HTMLSummaryAccessible* summaryAccessible = nullptr; + for (uint32_t i = 0; i < details->ChildCount(); i++) { + // Iterate through the children of our details accessible to locate main + // summary. This iteration includes the anonymous summary if the details + // element was not explicitly created with one. + LocalAccessible* child = details->LocalChildAt(i); + auto* summary = + mozilla::dom::HTMLSummaryElement::FromNodeOrNull(child->GetContent()); + if (summary && summary->IsMainSummary()) { + summaryAccessible = static_cast(child); + break; + } + } + + return summaryAccessible; +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLSummaryAccessible: Widgets + +bool HTMLSummaryAccessible::IsWidget() const { return true; } + +//////////////////////////////////////////////////////////////////////////////// +// HTMLHeaderOrFooterAccessible +//////////////////////////////////////////////////////////////////////////////// + +role HTMLHeaderOrFooterAccessible::NativeRole() const { + // Only map header and footer if they are direct descendants of the body tag. + // If other sectioning or sectioning root elements, they become sections. + nsIContent* parent = mContent->GetParent(); + while (parent) { + if (parent->IsAnyOfHTMLElements( + nsGkAtoms::article, nsGkAtoms::aside, nsGkAtoms::nav, + nsGkAtoms::section, nsGkAtoms::main, nsGkAtoms::blockquote, + nsGkAtoms::details, nsGkAtoms::dialog, nsGkAtoms::fieldset, + nsGkAtoms::figure, nsGkAtoms::td)) { + break; + } + parent = parent->GetParent(); + } + + // No sectioning or sectioning root elements found. + if (!parent) { + return roles::LANDMARK; + } + + return roles::SECTION; +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLSectionAccessible +//////////////////////////////////////////////////////////////////////////////// + +role HTMLSectionAccessible::NativeRole() const { + nsAutoString name; + const_cast(this)->Name(name); + return name.IsEmpty() ? roles::SECTION : roles::REGION; +} diff --git a/accessible/html/HTMLElementAccessibles.h b/accessible/html/HTMLElementAccessibles.h new file mode 100644 index 0000000000..5a3ec6cef8 --- /dev/null +++ b/accessible/html/HTMLElementAccessibles.h @@ -0,0 +1,159 @@ +/* -*- 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) {} + + // LocalAccessible + virtual a11y::role NativeRole() const override; +}; + +/** + * Used for HTML br element. + */ +class HTMLBRAccessible : public LeafAccessible { + public: + HTMLBRAccessible(nsIContent* aContent, DocAccessible* aDoc) + : LeafAccessible(aContent, aDoc) { + mType = eHTMLBRType; + mGenericTypes |= eText; + } + + // LocalAccessible + virtual a11y::role NativeRole() const override; + virtual uint64_t NativeState() const override; + + protected: + // LocalAccessible + virtual ENameValueFlag NativeName(nsString& aName) const override; +}; + +/** + * Used for HTML label element. + */ +class HTMLLabelAccessible : public HyperTextAccessible { + public: + HTMLLabelAccessible(nsIContent* aContent, DocAccessible* aDoc) + : HyperTextAccessible(aContent, aDoc) {} + + NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLLabelAccessible, HyperTextAccessible) + + // LocalAccessible + virtual Relation RelationByType(RelationType aType) const override; + + // ActionAccessible + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool HasPrimaryAction() const override; + + protected: + virtual ~HTMLLabelAccessible() {} + virtual ENameValueFlag NativeName(nsString& aName) const override; + virtual void DOMAttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue, + uint64_t aOldState) override; +}; + +/** + * Used for HTML output element. + */ +class HTMLOutputAccessible : public HyperTextAccessible { + public: + HTMLOutputAccessible(nsIContent* aContent, DocAccessible* aDoc) + : HyperTextAccessible(aContent, aDoc) {} + + NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLOutputAccessible, + HyperTextAccessible) + + // LocalAccessible + virtual Relation RelationByType(RelationType aType) const override; + + virtual void DOMAttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue, + uint64_t aOldState) override; + + protected: + virtual ~HTMLOutputAccessible() {} +}; + +/** + * Accessible for the HTML summary element. + */ +class HTMLSummaryAccessible : public HyperTextAccessible { + public: + enum { eAction_Click = 0 }; + + HTMLSummaryAccessible(nsIContent* aContent, DocAccessible* aDoc); + + // Check that the given LocalAccessible belongs to a details frame. + // If so, find and return the accessible for the detail frame's + // main summary. + static HTMLSummaryAccessible* FromDetails(LocalAccessible* aDetails); + + // LocalAccessible + virtual uint64_t NativeState() const override; + + // ActionAccessible + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + virtual bool HasPrimaryAction() const override; + + // Widgets + virtual bool IsWidget() const override; +}; + +/** + * Used for HTML header and footer elements. + */ +class HTMLHeaderOrFooterAccessible : public HyperTextAccessible { + public: + HTMLHeaderOrFooterAccessible(nsIContent* aContent, DocAccessible* aDoc) + : HyperTextAccessible(aContent, aDoc) {} + + NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLHeaderOrFooterAccessible, + HyperTextAccessible) + + // LocalAccessible + virtual a11y::role NativeRole() const override; + + protected: + virtual ~HTMLHeaderOrFooterAccessible() {} +}; + +/** + * Used for HTML section element. + */ +class HTMLSectionAccessible : public HyperTextAccessible { + public: + HTMLSectionAccessible(nsIContent* aContent, DocAccessible* aDoc) + : HyperTextAccessible(aContent, aDoc) {} + + NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLSectionAccessible, + HyperTextAccessible) + + // LocalAccessible + virtual a11y::role NativeRole() const override; + + protected: + virtual ~HTMLSectionAccessible() = default; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/html/HTMLFormControlAccessible.cpp b/accessible/html/HTMLFormControlAccessible.cpp new file mode 100644 index 0000000000..95d6fed7b3 --- /dev/null +++ b/accessible/html/HTMLFormControlAccessible.cpp @@ -0,0 +1,979 @@ +/* -*- 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 "CacheConstants.h" +#include "DocAccessible-inl.h" +#include "LocalAccessible-inl.h" +#include "nsAccUtils.h" +#include "nsEventShell.h" +#include "nsTextEquivUtils.h" +#include "Relation.h" +#include "mozilla/a11y/Role.h" +#include "States.h" +#include "TextLeafAccessible.h" + +#include "nsContentList.h" +#include "mozilla/dom/HTMLInputElement.h" +#include "mozilla/dom/HTMLTextAreaElement.h" +#include "mozilla/dom/HTMLFormControlsCollection.h" +#include "nsIFormControl.h" + +#include "mozilla/FloatingPoint.h" +#include "mozilla/Preferences.h" +#include "mozilla/TextEditor.h" + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// HTMLFormAccessible +//////////////////////////////////////////////////////////////////////////////// + +role HTMLFormAccessible::NativeRole() const { + nsAutoString name; + const_cast(this)->Name(name); + return name.IsEmpty() ? roles::FORM : roles::FORM_LANDMARK; +} + +void HTMLFormAccessible::DOMAttributeChanged(int32_t aNameSpaceID, + nsAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue, + uint64_t aOldState) { + HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType, + aOldValue, aOldState); + if (aAttribute == nsGkAtoms::autocomplete) { + dom::HTMLFormElement* formEl = dom::HTMLFormElement::FromNode(mContent); + + HTMLFormControlsCollection* controls = formEl->Elements(); + uint32_t length = controls->Length(); + for (uint32_t i = 0; i < length; i++) { + if (LocalAccessible* acc = mDoc->GetAccessible(controls->Item(i))) { + if (acc->IsTextField() && !acc->IsPassword()) { + if (!acc->Elm()->HasAttr(nsGkAtoms::list_) && + !acc->Elm()->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::autocomplete, nsGkAtoms::OFF, + eIgnoreCase)) { + RefPtr stateChangeEvent = + new AccStateChangeEvent(acc, states::SUPPORTS_AUTOCOMPLETION); + mDoc->FireDelayedEvent(stateChangeEvent); + } + } + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLRadioButtonAccessible +//////////////////////////////////////////////////////////////////////////////// + +uint64_t HTMLRadioButtonAccessible::NativeState() const { + uint64_t state = AccessibleWrap::NativeState(); + + state |= states::CHECKABLE; + + HTMLInputElement* input = HTMLInputElement::FromNode(mContent); + if (input && input->Checked()) state |= states::CHECKED; + + return state; +} + +void HTMLRadioButtonAccessible::GetPositionAndSetSize(int32_t* aPosInSet, + int32_t* aSetSize) { + Unused << ComputeGroupAttributes(aPosInSet, aSetSize); +} + +void HTMLRadioButtonAccessible::DOMAttributeChanged( + int32_t aNameSpaceID, nsAtom* aAttribute, int32_t aModType, + const nsAttrValue* aOldValue, uint64_t aOldState) { + if (aAttribute == nsGkAtoms::name) { + // If our name changed, it's possible our MEMBER_OF relation + // also changed. Push a cache update for Relations. + mDoc->QueueCacheUpdate(this, CacheDomain::Relations); + } else { + // Otherwise, handle this attribute change the way our parent + // class wants us to handle it. + RadioButtonAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, + aModType, aOldValue, aOldState); + } +} + +Relation HTMLRadioButtonAccessible::ComputeGroupAttributes( + int32_t* aPosInSet, int32_t* aSetSize) const { + Relation rel = Relation(); + int32_t namespaceId = mContent->NodeInfo()->NamespaceID(); + nsAutoString tagName; + mContent->NodeInfo()->GetName(tagName); + + nsAutoString type; + mContent->AsElement()->GetAttr(nsGkAtoms::type, type); + nsAutoString name; + mContent->AsElement()->GetAttr(nsGkAtoms::name, name); + + RefPtr inputElms; + + nsCOMPtr formControlNode(do_QueryInterface(mContent)); + if (dom::Element* formElm = formControlNode->GetForm()) { + inputElms = NS_GetContentList(formElm, namespaceId, tagName); + } else { + inputElms = NS_GetContentList(mContent->OwnerDoc(), namespaceId, tagName); + } + NS_ENSURE_TRUE(inputElms, rel); + + 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->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + type, eCaseMatters) && + inputElm->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, + name, eCaseMatters) && + mDoc->HasAccessible(inputElm)) { + count++; + rel.AppendTarget(mDoc->GetAccessible(inputElm)); + if (inputElm == mContent) indexOf = count; + } + } + + *aPosInSet = indexOf; + *aSetSize = count; + return rel; +} + +Relation HTMLRadioButtonAccessible::RelationByType(RelationType aType) const { + if (aType == RelationType::MEMBER_OF) { + int32_t unusedPos, unusedSetSize; + return ComputeGroupAttributes(&unusedPos, &unusedSetSize); + } + + return LocalAccessible::RelationByType(aType); +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLButtonAccessible +//////////////////////////////////////////////////////////////////////////////// + +HTMLButtonAccessible::HTMLButtonAccessible(nsIContent* aContent, + DocAccessible* aDoc) + : HyperTextAccessible(aContent, aDoc) { + mGenericTypes |= eButton; +} + +bool HTMLButtonAccessible::HasPrimaryAction() const { return true; } + +void HTMLButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) { + if (aIndex == eAction_Click) aName.AssignLiteral("press"); +} + +uint64_t HTMLButtonAccessible::NativeState() const { + uint64_t state = HyperTextAccessible::NativeState(); + + dom::Element* elm = Elm(); + if (auto* popover = elm->GetEffectivePopoverTargetElement()) { + LocalAccessible* popoverAcc = mDoc->GetAccessible(popover); + if (!popoverAcc || !popoverAcc->IsAncestorOf(this)) { + if (popover->IsPopoverOpen()) { + state |= states::EXPANDED; + } else { + state |= states::COLLAPSED; + } + } + } + + ElementState elmState = mContent->AsElement()->State(); + if (elmState.HasState(ElementState::DEFAULT)) state |= states::DEFAULT; + + return state; +} + +role HTMLButtonAccessible::NativeRole() const { return roles::PUSHBUTTON; } + +ENameValueFlag HTMLButtonAccessible::NativeName(nsString& aName) const { + // 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 = LocalAccessible::NativeName(aName); + if (!aName.IsEmpty() || !mContent->IsHTMLElement(nsGkAtoms::input) || + !mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + nsGkAtoms::image, eCaseMatters)) { + return nameFlag; + } + + if (!mContent->AsElement()->GetAttr(nsGkAtoms::alt, aName)) { + mContent->AsElement()->GetAttr(nsGkAtoms::value, aName); + } + + aName.CompressWhitespace(); + return eNameOK; +} + +void HTMLButtonAccessible::DOMAttributeChanged(int32_t aNameSpaceID, + nsAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue, + uint64_t aOldState) { + HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType, + aOldValue, aOldState); + + if (aAttribute == nsGkAtoms::value) { + dom::Element* elm = Elm(); + if (elm->IsHTMLElement(nsGkAtoms::input) || + (elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, nsGkAtoms::image, + eCaseMatters) && + !elm->HasAttr(nsGkAtoms::alt))) { + if (!nsAccUtils::HasARIAAttr(elm, nsGkAtoms::aria_labelledby) && + !nsAccUtils::HasARIAAttr(elm, nsGkAtoms::aria_label)) { + mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLButtonAccessible: Widgets + +bool HTMLButtonAccessible::IsWidget() const { return true; } + +//////////////////////////////////////////////////////////////////////////////// +// HTMLTextFieldAccessible +//////////////////////////////////////////////////////////////////////////////// + +HTMLTextFieldAccessible::HTMLTextFieldAccessible(nsIContent* aContent, + DocAccessible* aDoc) + : HyperTextAccessible(aContent, aDoc) { + mType = mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + nsGkAtoms::password, eIgnoreCase) + ? eHTMLTextPasswordFieldType + : eHTMLTextFieldType; +} + +role HTMLTextFieldAccessible::NativeRole() const { + if (mType == eHTMLTextPasswordFieldType) { + return roles::PASSWORD_TEXT; + } + if (mContent->AsElement()->HasAttr(nsGkAtoms::list_)) { + return roles::EDITCOMBOBOX; + } + return roles::ENTRY; +} + +already_AddRefed HTMLTextFieldAccessible::NativeAttributes() { + RefPtr attributes = HyperTextAccessible::NativeAttributes(); + + // Expose type for text input elements as it gives some useful context, + // especially for mobile. + if (const nsAttrValue* attr = + mContent->AsElement()->GetParsedAttr(nsGkAtoms::type)) { + RefPtr inputType = attr->GetAsAtom(); + if (inputType) { + if (!ARIARoleMap() && inputType == nsGkAtoms::search) { + attributes->SetAttribute(nsGkAtoms::xmlroles, nsGkAtoms::searchbox); + } + attributes->SetAttribute(nsGkAtoms::textInputType, inputType); + } + } + // If this element has the placeholder attribute set, + // and if that is not identical to the name, expose it as an object attribute. + nsString placeholderText; + if (mContent->AsElement()->GetAttr(nsGkAtoms::placeholder, placeholderText)) { + nsAutoString name; + const_cast(this)->Name(name); + if (!name.Equals(placeholderText)) { + attributes->SetAttribute(nsGkAtoms::placeholder, + std::move(placeholderText)); + } + } + + return attributes.forget(); +} + +ENameValueFlag HTMLTextFieldAccessible::Name(nsString& aName) const { + ENameValueFlag nameFlag = LocalAccessible::Name(aName); + if (!aName.IsEmpty()) return nameFlag; + + // text inputs and textareas might have useful placeholder text + mContent->AsElement()->GetAttr(nsGkAtoms::placeholder, aName); + return eNameOK; +} + +void HTMLTextFieldAccessible::Value(nsString& aValue) const { + aValue.Truncate(); + if (NativeState() & states::PROTECTED) { // Don't return password text! + return; + } + + HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(mContent); + if (textArea) { + textArea->GetValue(aValue); + return; + } + + HTMLInputElement* input = HTMLInputElement::FromNode(mContent); + if (input) { + // Pass NonSystem as the caller type, to be safe. We don't expect to have a + // file input here. + input->GetValue(aValue, CallerType::NonSystem); + } +} + +bool HTMLTextFieldAccessible::AttributeChangesState(nsAtom* aAttribute) { + if (aAttribute == nsGkAtoms::readonly || aAttribute == nsGkAtoms::list_ || + aAttribute == nsGkAtoms::autocomplete) { + return true; + } + + return LocalAccessible::AttributeChangesState(aAttribute); +} + +void HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState) const { + HyperTextAccessible::ApplyARIAState(aState); + aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState); +} + +uint64_t HTMLTextFieldAccessible::NativeState() const { + uint64_t state = HyperTextAccessible::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->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + nsGkAtoms::password, eIgnoreCase)) { + state |= states::PROTECTED; + } + + if (mContent->AsElement()->HasAttr(nsGkAtoms::readonly)) { + state |= states::READONLY; + } + + // Is it an or a