/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "XULElementAccessibles.h" #include "Accessible-inl.h" #include "BaseAccessibles.h" #include "DocAccessible-inl.h" #include "nsAccUtils.h" #include "nsCoreUtils.h" #include "nsTextEquivUtils.h" #include "Relation.h" #include "Role.h" #include "States.h" #include "TextUpdater.h" #ifdef A11Y_LOG # include "Logging.h" #endif #include "nsNameSpaceManager.h" #include "nsNetUtil.h" #include "nsString.h" #include "nsTextBoxFrame.h" #include "nsXULElement.h" using namespace mozilla::a11y; //////////////////////////////////////////////////////////////////////////////// // XULLabelAccessible //////////////////////////////////////////////////////////////////////////////// XULLabelAccessible::XULLabelAccessible(nsIContent* aContent, DocAccessible* aDoc) : HyperTextAccessibleWrap(aContent, aDoc) { mType = eXULLabelType; // If @value attribute is given then it's rendered instead text content. In // this case we need to create a text leaf accessible to make @value attribute // accessible. // XXX: text interface doesn't let you get the text by words. nsTextBoxFrame* textBoxFrame = do_QueryFrame(mContent->GetPrimaryFrame()); if (textBoxFrame) { mValueTextLeaf = new XULLabelTextLeafAccessible(mContent, mDoc); mDoc->BindToDocument(mValueTextLeaf, nullptr); nsAutoString text; textBoxFrame->GetCroppedTitle(text); mValueTextLeaf->SetText(text); AppendChild(mValueTextLeaf); } } void XULLabelAccessible::Shutdown() { mValueTextLeaf = nullptr; HyperTextAccessibleWrap::Shutdown(); } void XULLabelAccessible::DispatchClickEvent(nsIContent* aContent, uint32_t aActionIndex) const { // Bug 1578140: For labels inside buttons, The base implementation of // DispatchClickEvent doesn't fire a command event on the button. RefPtr el = nsXULElement::FromNodeOrNull(aContent); if (el) { el->Click(mozilla::dom::CallerType::System); } } ENameValueFlag XULLabelAccessible::NativeName(nsString& aName) const { // if the value attr doesn't exist, the screen reader must get the accessible // text from the accessible text interface or from the children if (mValueTextLeaf) return mValueTextLeaf->Name(aName); return Accessible::NativeName(aName); } role XULLabelAccessible::NativeRole() const { return roles::LABEL; } uint64_t XULLabelAccessible::NativeState() const { // Labels and description have read only state // They are not focusable or selectable return HyperTextAccessibleWrap::NativeState() | states::READONLY; } Relation XULLabelAccessible::RelationByType(RelationType aType) const { Relation rel = HyperTextAccessibleWrap::RelationByType(aType); // The label for xul:groupbox is generated from the first xul:label if (aType == RelationType::LABEL_FOR) { Accessible* parent = Parent(); if (parent && parent->Role() == roles::GROUPING && parent->GetChildAt(0) == this) { nsIContent* parentContent = parent->GetContent(); if (parentContent && parentContent->IsXULElement(nsGkAtoms::groupbox)) { rel.AppendTarget(parent); } } } return rel; } void XULLabelAccessible::UpdateLabelValue(const nsString& aValue) { #ifdef A11Y_LOG if (logging::IsEnabled(logging::eText)) { logging::MsgBegin("TEXT", "text may be changed (xul:label @value update)"); logging::Node("container", mContent); logging::MsgEntry("old text '%s'", NS_ConvertUTF16toUTF8(mValueTextLeaf->Text()).get()); logging::MsgEntry("new text: '%s'", NS_ConvertUTF16toUTF8(aValue).get()); logging::MsgEnd(); } #endif TextUpdater::Run(mDoc, mValueTextLeaf, aValue); } //////////////////////////////////////////////////////////////////////////////// // XULLabelTextLeafAccessible //////////////////////////////////////////////////////////////////////////////// role XULLabelTextLeafAccessible::NativeRole() const { return roles::TEXT_LEAF; } uint64_t XULLabelTextLeafAccessible::NativeState() const { return TextLeafAccessibleWrap::NativeState() | states::READONLY; } //////////////////////////////////////////////////////////////////////////////// // XULTooltipAccessible //////////////////////////////////////////////////////////////////////////////// XULTooltipAccessible::XULTooltipAccessible(nsIContent* aContent, DocAccessible* aDoc) : LeafAccessible(aContent, aDoc) { mType = eXULTooltipType; } uint64_t XULTooltipAccessible::NativeState() const { return LeafAccessible::NativeState() | states::READONLY; } role XULTooltipAccessible::NativeRole() const { return roles::TOOLTIP; } //////////////////////////////////////////////////////////////////////////////// // XULLinkAccessible //////////////////////////////////////////////////////////////////////////////// XULLinkAccessible::XULLinkAccessible(nsIContent* aContent, DocAccessible* aDoc) : XULLabelAccessible(aContent, aDoc) {} XULLinkAccessible::~XULLinkAccessible() {} //////////////////////////////////////////////////////////////////////////////// // XULLinkAccessible: Accessible void XULLinkAccessible::Value(nsString& aValue) const { aValue.Truncate(); mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::href, aValue); } ENameValueFlag XULLinkAccessible::NativeName(nsString& aName) const { mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName); if (!aName.IsEmpty()) return eNameOK; nsTextEquivUtils::GetNameFromSubtree(this, aName); return aName.IsEmpty() ? eNameOK : eNameFromSubtree; } role XULLinkAccessible::NativeRole() const { return roles::LINK; } uint64_t XULLinkAccessible::NativeLinkState() const { return states::LINKED; } uint8_t XULLinkAccessible::ActionCount() const { return 1; } void XULLinkAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) { aName.Truncate(); if (aIndex == eAction_Jump) aName.AssignLiteral("jump"); } bool XULLinkAccessible::DoAction(uint8_t aIndex) const { if (aIndex != eAction_Jump) return false; DoCommand(); return true; } //////////////////////////////////////////////////////////////////////////////// // XULLinkAccessible: HyperLinkAccessible bool XULLinkAccessible::IsLink() const { // Expose HyperLinkAccessible unconditionally. return true; } uint32_t XULLinkAccessible::StartOffset() { // If XUL link accessible is not contained by hypertext accessible then // start offset matches index in parent because the parent doesn't contains // a text. // XXX: accessible parent of XUL link accessible should be a hypertext // accessible. if (Accessible::IsLink()) return Accessible::StartOffset(); return IndexInParent(); } uint32_t XULLinkAccessible::EndOffset() { if (Accessible::IsLink()) return Accessible::EndOffset(); return IndexInParent() + 1; } already_AddRefed XULLinkAccessible::AnchorURIAt( uint32_t aAnchorIndex) const { if (aAnchorIndex != 0) return nullptr; nsAutoString href; mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href); dom::Document* document = mContent->OwnerDoc(); nsCOMPtr anchorURI; NS_NewURI(getter_AddRefs(anchorURI), href, document->GetDocumentCharacterSet(), mContent->GetBaseURI()); return anchorURI.forget(); }