зеркало из https://github.com/mozilla/gecko-dev.git
446 строки
15 KiB
C++
446 строки
15 KiB
C++
/* -*- 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 "LocalAccessible-inl.h"
|
|
#include "HTMLFormControlAccessible.h"
|
|
#include "nsAccUtils.h"
|
|
#include "DocAccessible.h"
|
|
#include "Relation.h"
|
|
#include "mozilla/a11y/Role.h"
|
|
#include "States.h"
|
|
#include "TreeWalker.h"
|
|
#include "XULMenuAccessible.h"
|
|
|
|
#include "nsIDOMXULButtonElement.h"
|
|
#include "nsIDOMXULMenuListElement.h"
|
|
#include "nsIDOMXULRadioGroupElement.h"
|
|
#include "nsIDOMXULSelectCntrlItemEl.h"
|
|
#include "nsIFrame.h"
|
|
#include "nsMenuPopupFrame.h"
|
|
#include "nsNameSpaceManager.h"
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
using namespace mozilla::a11y;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULButtonAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
XULButtonAccessible::XULButtonAccessible(nsIContent* aContent,
|
|
DocAccessible* aDoc)
|
|
: AccessibleWrap(aContent, aDoc) {
|
|
if (ContainsMenu()) {
|
|
mGenericTypes |= eMenuButton;
|
|
} else {
|
|
mGenericTypes |= eButton;
|
|
}
|
|
}
|
|
|
|
XULButtonAccessible::~XULButtonAccessible() {}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULButtonAccessible: nsISupports
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULButtonAccessible: nsIAccessible
|
|
|
|
bool XULButtonAccessible::HasPrimaryAction() const { return true; }
|
|
|
|
void XULButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
|
|
if (aIndex == eAction_Click) aName.AssignLiteral("press");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULButtonAccessible: LocalAccessible
|
|
|
|
role XULButtonAccessible::NativeRole() const {
|
|
// Buttons can be checked; they simply appear pressed in rather than checked.
|
|
// In this case, we must expose them as toggle buttons.
|
|
nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
|
|
if (xulButtonElement) {
|
|
nsAutoString type;
|
|
xulButtonElement->GetType(type);
|
|
if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
|
|
return roles::TOGGLE_BUTTON;
|
|
}
|
|
}
|
|
return roles::PUSHBUTTON;
|
|
}
|
|
|
|
uint64_t XULButtonAccessible::NativeState() const {
|
|
// Possible states: focused, focusable, unavailable(disabled).
|
|
|
|
// get focus and disable status from base class
|
|
uint64_t state = LocalAccessible::NativeState();
|
|
|
|
nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
|
|
if (xulButtonElement) {
|
|
// Some buttons can have their checked state set without being of type
|
|
// checkbox or radio. Expose the pressed state unconditionally.
|
|
bool checked = false;
|
|
xulButtonElement->GetChecked(&checked);
|
|
if (checked) {
|
|
state |= states::PRESSED;
|
|
}
|
|
}
|
|
|
|
if (ContainsMenu()) state |= states::HASPOPUP;
|
|
|
|
if (mContent->AsElement()->HasAttr(nsGkAtoms::_default)) {
|
|
state |= states::DEFAULT;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
bool XULButtonAccessible::AttributeChangesState(nsAtom* aAttribute) {
|
|
if (aAttribute == nsGkAtoms::checked) {
|
|
return true;
|
|
}
|
|
return AccessibleWrap::AttributeChangesState(aAttribute);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULButtonAccessible: Widgets
|
|
|
|
bool XULButtonAccessible::IsWidget() const { return true; }
|
|
|
|
bool XULButtonAccessible::IsActiveWidget() const {
|
|
return FocusMgr()->HasDOMFocus(mContent);
|
|
}
|
|
|
|
bool XULButtonAccessible::AreItemsOperable() const {
|
|
if (IsMenuButton()) {
|
|
LocalAccessible* menuPopup = mChildren.SafeElementAt(0, nullptr);
|
|
if (menuPopup) {
|
|
nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
|
|
return menuPopupFrame->IsOpen();
|
|
}
|
|
}
|
|
return false; // no items
|
|
}
|
|
|
|
bool XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
|
|
// In general XUL buttons should not have accessible children. However:
|
|
return
|
|
// menu buttons can have popup accessibles (@type="menu" or
|
|
// columnpicker).
|
|
aEl->IsXULElement(nsGkAtoms::menupopup) ||
|
|
// A XUL button can be labelled by a direct child text node, so we need to
|
|
// allow that as a child so it will be picked up when computing name from
|
|
// subtree.
|
|
(aEl->IsText() && aEl->GetParent() == mContent);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULButtonAccessible protected
|
|
|
|
bool XULButtonAccessible::ContainsMenu() const {
|
|
return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
|
nsGkAtoms::menu, eCaseMatters);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULDropmarkerAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
XULDropmarkerAccessible::XULDropmarkerAccessible(nsIContent* aContent,
|
|
DocAccessible* aDoc)
|
|
: LeafAccessible(aContent, aDoc) {}
|
|
|
|
bool XULDropmarkerAccessible::HasPrimaryAction() const { return true; }
|
|
|
|
bool XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const {
|
|
bool isOpen = false;
|
|
|
|
nsIContent* parent = mContent->GetFlattenedTreeParent();
|
|
|
|
while (parent) {
|
|
nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
|
|
parent->AsElement()->AsXULButton();
|
|
if (parentButtonElement) {
|
|
parentButtonElement->GetOpen(&isOpen);
|
|
if (aToggleOpen) parentButtonElement->SetOpen(!isOpen);
|
|
return isOpen;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
|
|
parent->AsElement()->AsXULMenuList();
|
|
if (parentMenuListElement) {
|
|
parentMenuListElement->GetOpen(&isOpen);
|
|
if (aToggleOpen) parentMenuListElement->SetOpen(!isOpen);
|
|
return isOpen;
|
|
}
|
|
parent = parent->GetFlattenedTreeParent();
|
|
}
|
|
|
|
return isOpen;
|
|
}
|
|
|
|
void XULDropmarkerAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
|
|
aName.Truncate();
|
|
if (aIndex == eAction_Click) {
|
|
if (DropmarkerOpen(false)) {
|
|
aName.AssignLiteral("close");
|
|
} else {
|
|
aName.AssignLiteral("open");
|
|
}
|
|
}
|
|
}
|
|
|
|
bool XULDropmarkerAccessible::DoAction(uint8_t index) const {
|
|
if (index == eAction_Click) {
|
|
DropmarkerOpen(true); // Reverse the open attribute
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
role XULDropmarkerAccessible::NativeRole() const { return roles::PUSHBUTTON; }
|
|
|
|
uint64_t XULDropmarkerAccessible::NativeState() const {
|
|
return DropmarkerOpen(false) ? states::PRESSED : 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULGroupboxAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
XULGroupboxAccessible::XULGroupboxAccessible(nsIContent* aContent,
|
|
DocAccessible* aDoc)
|
|
: AccessibleWrap(aContent, aDoc) {}
|
|
|
|
role XULGroupboxAccessible::NativeRole() const { return roles::GROUPING; }
|
|
|
|
ENameValueFlag XULGroupboxAccessible::NativeName(nsString& aName) const {
|
|
// XXX: we use the first related accessible only.
|
|
LocalAccessible* label =
|
|
RelationByType(RelationType::LABELLED_BY).LocalNext();
|
|
if (label) return label->Name(aName);
|
|
|
|
return eNameOK;
|
|
}
|
|
|
|
Relation XULGroupboxAccessible::RelationByType(RelationType aType) const {
|
|
Relation rel = AccessibleWrap::RelationByType(aType);
|
|
|
|
// The label for xul:groupbox is generated from the first xul:label
|
|
if (aType == RelationType::LABELLED_BY && ChildCount() > 0) {
|
|
LocalAccessible* childAcc = LocalChildAt(0);
|
|
if (childAcc->Role() == roles::LABEL &&
|
|
childAcc->GetContent()->IsXULElement(nsGkAtoms::label)) {
|
|
rel.AppendTarget(childAcc);
|
|
}
|
|
}
|
|
|
|
return rel;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULRadioButtonAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
XULRadioButtonAccessible::XULRadioButtonAccessible(nsIContent* aContent,
|
|
DocAccessible* aDoc)
|
|
: RadioButtonAccessible(aContent, aDoc) {}
|
|
|
|
uint64_t XULRadioButtonAccessible::NativeState() const {
|
|
uint64_t state = LeafAccessible::NativeState();
|
|
state |= states::CHECKABLE;
|
|
|
|
nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
|
|
Elm()->AsXULSelectControlItem();
|
|
if (radioButton) {
|
|
bool selected = false; // Radio buttons can be selected
|
|
radioButton->GetSelected(&selected);
|
|
if (selected) {
|
|
state |= states::CHECKED;
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
uint64_t XULRadioButtonAccessible::NativeInteractiveState() const {
|
|
return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULRadioButtonAccessible: Widgets
|
|
|
|
LocalAccessible* XULRadioButtonAccessible::ContainerWidget() const {
|
|
return mParent;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULRadioGroupAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* XUL Radio Group
|
|
* The Radio Group proxies for the Radio Buttons themselves. The Group gets
|
|
* focus whereas the Buttons do not. So we only have an accessible object for
|
|
* this for the purpose of getting the proper RadioButton. Need this here to
|
|
* avoid circular reference problems when navigating the accessible tree and
|
|
* for getting to the radiobuttons.
|
|
*/
|
|
|
|
XULRadioGroupAccessible::XULRadioGroupAccessible(nsIContent* aContent,
|
|
DocAccessible* aDoc)
|
|
: XULSelectControlAccessible(aContent, aDoc) {}
|
|
|
|
role XULRadioGroupAccessible::NativeRole() const { return roles::RADIO_GROUP; }
|
|
|
|
uint64_t XULRadioGroupAccessible::NativeInteractiveState() const {
|
|
// The radio group is not focusable. Sometimes the focus controller will
|
|
// report that it is focused. That means that the actual selected radio button
|
|
// should be considered focused.
|
|
return NativelyUnavailable() ? states::UNAVAILABLE : 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULRadioGroupAccessible: Widgets
|
|
|
|
bool XULRadioGroupAccessible::IsWidget() const { return true; }
|
|
|
|
bool XULRadioGroupAccessible::IsActiveWidget() const {
|
|
return FocusMgr()->HasDOMFocus(mContent);
|
|
}
|
|
|
|
bool XULRadioGroupAccessible::AreItemsOperable() const { return true; }
|
|
|
|
LocalAccessible* XULRadioGroupAccessible::CurrentItem() const {
|
|
if (!mSelectControl) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<dom::Element> currentItemElm;
|
|
nsCOMPtr<nsIDOMXULRadioGroupElement> group =
|
|
mSelectControl->AsXULRadioGroup();
|
|
if (group) {
|
|
group->GetFocusedItem(getter_AddRefs(currentItemElm));
|
|
}
|
|
|
|
if (currentItemElm) {
|
|
DocAccessible* document = Document();
|
|
if (document) {
|
|
return document->GetAccessible(currentItemElm);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void XULRadioGroupAccessible::SetCurrentItem(const LocalAccessible* aItem) {
|
|
if (!mSelectControl) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<dom::Element> itemElm = aItem->Elm();
|
|
nsCOMPtr<nsIDOMXULRadioGroupElement> group =
|
|
mSelectControl->AsXULRadioGroup();
|
|
if (group) {
|
|
group->SetFocusedItem(itemElm);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULStatusBarAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
XULStatusBarAccessible::XULStatusBarAccessible(nsIContent* aContent,
|
|
DocAccessible* aDoc)
|
|
: AccessibleWrap(aContent, aDoc) {}
|
|
|
|
role XULStatusBarAccessible::NativeRole() const { return roles::STATUSBAR; }
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULToolbarButtonAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
XULToolbarButtonAccessible::XULToolbarButtonAccessible(nsIContent* aContent,
|
|
DocAccessible* aDoc)
|
|
: XULButtonAccessible(aContent, aDoc) {}
|
|
|
|
void XULToolbarButtonAccessible::GetPositionAndSetSize(int32_t* aPosInSet,
|
|
int32_t* aSetSize) {
|
|
int32_t setSize = 0;
|
|
int32_t posInSet = 0;
|
|
|
|
LocalAccessible* parent = LocalParent();
|
|
if (!parent) return;
|
|
|
|
uint32_t childCount = parent->ChildCount();
|
|
for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
|
|
LocalAccessible* child = parent->LocalChildAt(childIdx);
|
|
if (IsSeparator(child)) { // end of a group of buttons
|
|
if (posInSet) break; // we've found our group, so we're done
|
|
|
|
setSize = 0; // not our group, so start a new group
|
|
|
|
} else {
|
|
setSize++; // another button in the group
|
|
|
|
if (child == this) posInSet = setSize; // we've found our button
|
|
}
|
|
}
|
|
|
|
*aPosInSet = posInSet;
|
|
*aSetSize = setSize;
|
|
}
|
|
|
|
bool XULToolbarButtonAccessible::IsSeparator(LocalAccessible* aAccessible) {
|
|
nsIContent* content = aAccessible->GetContent();
|
|
return content && content->IsAnyOfXULElements(nsGkAtoms::toolbarseparator,
|
|
nsGkAtoms::toolbarspacer,
|
|
nsGkAtoms::toolbarspring);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULToolbarButtonAccessible: Widgets
|
|
|
|
bool XULToolbarButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
|
|
return XULButtonAccessible::IsAcceptableChild(aEl) ||
|
|
// In addition to the children allowed by buttons, toolbarbuttons can
|
|
// have labels as children, but only if the label attribute is not
|
|
// present.
|
|
(aEl->IsXULElement(nsGkAtoms::label) &&
|
|
!mContent->AsElement()->HasAttr(nsGkAtoms::label));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULToolbarAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
XULToolbarAccessible::XULToolbarAccessible(nsIContent* aContent,
|
|
DocAccessible* aDoc)
|
|
: AccessibleWrap(aContent, aDoc) {}
|
|
|
|
role XULToolbarAccessible::NativeRole() const { return roles::TOOLBAR; }
|
|
|
|
ENameValueFlag XULToolbarAccessible::NativeName(nsString& aName) const {
|
|
if (mContent->AsElement()->GetAttr(nsGkAtoms::toolbarname, aName)) {
|
|
aName.CompressWhitespace();
|
|
}
|
|
|
|
return eNameOK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// XULToolbarAccessible
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
XULToolbarSeparatorAccessible::XULToolbarSeparatorAccessible(
|
|
nsIContent* aContent, DocAccessible* aDoc)
|
|
: LeafAccessible(aContent, aDoc) {}
|
|
|
|
role XULToolbarSeparatorAccessible::NativeRole() const {
|
|
return roles::SEPARATOR;
|
|
}
|
|
|
|
uint64_t XULToolbarSeparatorAccessible::NativeState() const { return 0; }
|