diff -r 000000000000 -r 6474c204b198 accessible/src/xul/XULFormControlAccessible.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/accessible/src/xul/XULFormControlAccessible.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,641 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "XULFormControlAccessible.h" + +#include "Accessible-inl.h" +#include "HTMLFormControlAccessible.h" +#include "nsAccUtils.h" +#include "nsCoreUtils.h" +#include "DocAccessible.h" +#include "nsIAccessibleRelation.h" +#include "Relation.h" +#include "Role.h" +#include "States.h" +#include "TreeWalker.h" +#include "XULMenuAccessible.h" + +#include "nsIDOMNSEditableElement.h" +#include "nsIDOMXULButtonElement.h" +#include "nsIDOMXULCheckboxElement.h" +#include "nsIDOMXULMenuListElement.h" +#include "nsIDOMXULSelectCntrlItemEl.h" +#include "nsIDOMXULTextboxElement.h" +#include "nsIEditor.h" +#include "nsIFrame.h" +#include "nsITextControlFrame.h" +#include "nsMenuPopupFrame.h" +#include "nsNameSpaceManager.h" +#include "mozilla/dom/Element.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULButtonAccessible:: + XULButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ + if (ContainsMenu()) { + mGenericTypes |= eMenuButton; + } else { + mGenericTypes |= eButton; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible: nsISupports + +NS_IMPL_ISUPPORTS_INHERITED0(XULButtonAccessible, Accessible) + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible: nsIAccessible + +uint8_t +XULButtonAccessible::ActionCount() +{ + return 1; +} + +NS_IMETHODIMP +XULButtonAccessible::GetActionName(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click) { + aName.AssignLiteral("press"); + return NS_OK; + } + return NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +XULButtonAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != 0) + return NS_ERROR_INVALID_ARG; + + DoCommand(); + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible: Accessible + +role +XULButtonAccessible::NativeRole() +{ + return roles::PUSHBUTTON; +} + +uint64_t +XULButtonAccessible::NativeState() +{ + // Possible states: focused, focusable, unavailable(disabled). + + // get focus and disable status from base class + uint64_t state = Accessible::NativeState(); + + // Buttons can be checked -- they simply appear pressed in rather than checked + nsCOMPtr xulButtonElement(do_QueryInterface(mContent)); + if (xulButtonElement) { + nsAutoString type; + xulButtonElement->GetType(type); + if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) { + state |= states::CHECKABLE; + bool checked = false; + int32_t checkState = 0; + xulButtonElement->GetChecked(&checked); + if (checked) { + state |= states::PRESSED; + xulButtonElement->GetCheckState(&checkState); + if (checkState == nsIDOMXULButtonElement::CHECKSTATE_MIXED) { + state |= states::MIXED; + } + } + } + } + + if (ContainsMenu()) + state |= states::HASPOPUP; + + if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_default)) + state |= states::DEFAULT; + + return state; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible: Widgets + +bool +XULButtonAccessible::IsWidget() const +{ + return true; +} + +bool +XULButtonAccessible::IsActiveWidget() const +{ + return FocusMgr()->HasDOMFocus(mContent); +} + +bool +XULButtonAccessible::AreItemsOperable() const +{ + if (IsMenuButton()) { + Accessible* menuPopup = mChildren.SafeElementAt(0, nullptr); + if (menuPopup) { + nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame()); + return menuPopupFrame->IsOpen(); + } + } + return false; // no items +} + +Accessible* +XULButtonAccessible::ContainerWidget() const +{ + if (IsMenuButton() && mParent && mParent->IsAutoComplete()) + return mParent; + return nullptr; +} + +bool +XULButtonAccessible::IsAcceptableChild(Accessible* aPossibleChild) const +{ + // In general XUL button has not accessible children. Nevertheless menu + // buttons can have button (@type="menu-button") and popup accessibles + // (@type="menu-button", @type="menu" or columnpicker. + + // XXX: no children until the button is menu button. Probably it's not + // totally correct but in general AT wants to have leaf buttons. + roles::Role role = aPossibleChild->Role(); + + // Get an accessible for menupopup or panel elements. + if (role == roles::MENUPOPUP) + return true; + + // Button type="menu-button" contains a real button. Get an accessible + // for it. Ignore dropmarker button which is placed as a last child. + if (role != roles::PUSHBUTTON || + aPossibleChild->GetContent()->Tag() == nsGkAtoms::dropMarker) + return false; + + return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + nsGkAtoms::menuButton, eCaseMatters); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULButtonAccessible protected + +bool +XULButtonAccessible::ContainsMenu() +{ + static nsIContent::AttrValuesArray strings[] = + {&nsGkAtoms::menu, &nsGkAtoms::menuButton, nullptr}; + + return mContent->FindAttrValueIn(kNameSpaceID_None, + nsGkAtoms::type, + strings, eCaseMatters) >= 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULDropmarkerAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULDropmarkerAccessible:: + XULDropmarkerAccessible(nsIContent* aContent, DocAccessible* aDoc) : + LeafAccessible(aContent, aDoc) +{ +} + +uint8_t +XULDropmarkerAccessible::ActionCount() +{ + return 1; +} + +bool +XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) +{ + bool isOpen = false; + + nsCOMPtr parentButtonElement = + do_QueryInterface(mContent->GetFlattenedTreeParent()); + + if (parentButtonElement) { + parentButtonElement->GetOpen(&isOpen); + if (aToggleOpen) + parentButtonElement->SetOpen(!isOpen); + } + else { + nsCOMPtr parentMenuListElement = + do_QueryInterface(parentButtonElement); + if (parentMenuListElement) { + parentMenuListElement->GetOpen(&isOpen); + if (aToggleOpen) + parentMenuListElement->SetOpen(!isOpen); + } + } + + return isOpen; +} + +/** + * Return the name of our only action + */ +NS_IMETHODIMP +XULDropmarkerAccessible::GetActionName(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click) { + if (DropmarkerOpen(false)) + aName.AssignLiteral("close"); + else + aName.AssignLiteral("open"); + return NS_OK; + } + + return NS_ERROR_INVALID_ARG; +} + +/** + * Tell the Dropmarker to do its action + */ +NS_IMETHODIMP +XULDropmarkerAccessible::DoAction(uint8_t index) +{ + if (index == eAction_Click) { + DropmarkerOpen(true); // Reverse the open attribute + return NS_OK; + } + return NS_ERROR_INVALID_ARG; +} + +role +XULDropmarkerAccessible::NativeRole() +{ + return roles::PUSHBUTTON; +} + +uint64_t +XULDropmarkerAccessible::NativeState() +{ + return DropmarkerOpen(false) ? states::PRESSED : 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULCheckboxAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULCheckboxAccessible:: + XULCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc) : + LeafAccessible(aContent, aDoc) +{ +} + +role +XULCheckboxAccessible::NativeRole() +{ + return roles::CHECKBUTTON; +} + +uint8_t +XULCheckboxAccessible::ActionCount() +{ + return 1; +} + +/** + * Return the name of our only action + */ +NS_IMETHODIMP +XULCheckboxAccessible::GetActionName(uint8_t aIndex, nsAString& aName) +{ + if (aIndex == eAction_Click) { + // check or uncheck + + if (NativeState() & states::CHECKED) + aName.AssignLiteral("uncheck"); + else + aName.AssignLiteral("check"); + + return NS_OK; + } + return NS_ERROR_INVALID_ARG; +} + +/** + * Tell the checkbox to do its only action -- check( or uncheck) itself + */ +NS_IMETHODIMP +XULCheckboxAccessible::DoAction(uint8_t aIndex) +{ + if (aIndex != eAction_Click) + return NS_ERROR_INVALID_ARG; + + DoCommand(); + return NS_OK; +} + +uint64_t +XULCheckboxAccessible::NativeState() +{ + // Possible states: focused, focusable, unavailable(disabled), checked + // Get focus and disable status from base class + uint64_t state = LeafAccessible::NativeState(); + + state |= states::CHECKABLE; + + // Determine Checked state + nsCOMPtr xulCheckboxElement = + do_QueryInterface(mContent); + if (xulCheckboxElement) { + bool checked = false; + xulCheckboxElement->GetChecked(&checked); + if (checked) { + state |= states::CHECKED; + int32_t checkState = 0; + xulCheckboxElement->GetCheckState(&checkState); + if (checkState == nsIDOMXULCheckboxElement::CHECKSTATE_MIXED) + state |= states::MIXED; + } + } + + return state; +} + +//////////////////////////////////////////////////////////////////////////////// +// XULGroupboxAccessible +//////////////////////////////////////////////////////////////////////////////// + +XULGroupboxAccessible:: + XULGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +role +XULGroupboxAccessible::NativeRole() +{ + return roles::GROUPING; +} + +ENameValueFlag +XULGroupboxAccessible::NativeName(nsString& aName) +{ + // XXX: we use the first related accessible only. + Accessible* label = + RelationByType(RelationType::LABELLED_BY).Next(); + if (label) + return label->Name(aName); + + return eNameOK; +} + +Relation +XULGroupboxAccessible::RelationByType(RelationType aType) +{ + Relation rel = AccessibleWrap::RelationByType(aType); + if (aType != RelationType::LABELLED_BY) + return rel; + + // The label for xul:groupbox is generated from xul:label that is + // inside the anonymous content of the xul:caption. + // The xul:label has an accessible object but the xul:caption does not + uint32_t childCount = ChildCount(); + for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { + Accessible* childAcc = GetChildAt(childIdx); + if (childAcc->Role() == roles::LABEL) { + // Ensure that it's our label + Relation reverseRel = childAcc->RelationByType(RelationType::LABEL_FOR); + Accessible* testGroupbox = nullptr; + while ((testGroupbox = reverseRel.Next())) + if (testGroupbox == this) { + // The