/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Aaron Leventhal (aaronl@netscape.com) * Kyle Yuan (kyle.yuan@sun.com) * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // NOTE: alphabetically ordered #include "nsXULFormControlAccessible.h" #include "nsHTMLFormControlAccessible.h" #include "nsAccessibilityAtoms.h" #include "nsAccessibleTreeWalker.h" #include "nsXULMenuAccessible.h" #include "nsIDOMHTMLInputElement.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 "nsINameSpaceManager.h" #include "nsITextControlFrame.h" #include "nsIPresShell.h" //////////////////////////////////////////////////////////////////////////////// // nsXULButtonAccessible //////////////////////////////////////////////////////////////////////////////// nsXULButtonAccessible:: nsXULButtonAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell) : nsAccessibleWrap(aNode, aShell) { } //////////////////////////////////////////////////////////////////////////////// // nsXULButtonAccessible: nsISupports NS_IMPL_ISUPPORTS_INHERITED0(nsXULButtonAccessible, nsAccessible) //////////////////////////////////////////////////////////////////////////////// // nsXULButtonAccessible: nsIAccessible NS_IMETHODIMP nsXULButtonAccessible::GetNumActions(PRUint8 *aCount) { NS_ENSURE_ARG_POINTER(aCount); *aCount = 1; return NS_OK; } NS_IMETHODIMP nsXULButtonAccessible::GetActionName(PRUint8 aIndex, nsAString& aName) { if (aIndex == eAction_Click) { aName.AssignLiteral("press"); return NS_OK; } return NS_ERROR_INVALID_ARG; } NS_IMETHODIMP nsXULButtonAccessible::DoAction(PRUint8 aIndex) { if (aIndex == 0) return DoCommand(); return NS_ERROR_INVALID_ARG; } //////////////////////////////////////////////////////////////////////////////// // nsXULButtonAccessible: nsAccessNode nsresult nsXULButtonAccessible::Init() { nsresult rv = nsAccessibleWrap::Init(); NS_ENSURE_SUCCESS(rv, rv); if (ContainsMenu()) nsCoreUtils::GeneratePopupTree(mDOMNode); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULButtonAccessible: nsAccessible nsresult nsXULButtonAccessible::GetRoleInternal(PRUint32 *aRole) { *aRole = nsIAccessibleRole::ROLE_PUSHBUTTON; return NS_OK; } nsresult nsXULButtonAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { // Possible states: focused, focusable, unavailable(disabled). // get focus and disable status from base class nsresult rv = nsAccessible::GetStateInternal(aState, aExtraState); NS_ENSURE_A11Y_SUCCESS(rv, rv); PRBool disabled = PR_FALSE; nsCOMPtr xulFormElement(do_QueryInterface(mDOMNode)); if (xulFormElement) { xulFormElement->GetDisabled(&disabled); if (disabled) *aState |= nsIAccessibleStates::STATE_UNAVAILABLE; else *aState |= nsIAccessibleStates::STATE_FOCUSABLE; } // Buttons can be checked -- they simply appear pressed in rather than checked nsCOMPtr xulButtonElement(do_QueryInterface(mDOMNode)); if (xulButtonElement) { nsAutoString type; xulButtonElement->GetType(type); if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) { *aState |= nsIAccessibleStates::STATE_CHECKABLE; PRBool checked = PR_FALSE; PRInt32 checkState = 0; xulButtonElement->GetChecked(&checked); if (checked) { *aState |= nsIAccessibleStates::STATE_PRESSED; xulButtonElement->GetCheckState(&checkState); if (checkState == nsIDOMXULButtonElement::CHECKSTATE_MIXED) { *aState |= nsIAccessibleStates::STATE_MIXED; } } } } if (ContainsMenu()) *aState |= nsIAccessibleStates::STATE_HASPOPUP; nsCOMPtr content(do_QueryInterface(mDOMNode)); if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::_default)) *aState |= nsIAccessibleStates::STATE_DEFAULT; return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULButtonAccessible: nsAccessible protected void nsXULButtonAccessible::CacheChildren() { // In general XUL button has not accessible children. Nevertheless menu // buttons can have button (@type="menu-button") and popup accessibles // (@type="menu-button" or @type="menu"). if (!mWeakShell) { mAccChildCount = eChildCountUninitialized; return; // This outer doc node has been shut down } if (mAccChildCount == eChildCountUninitialized) { mAccChildCount = 0; // Avoid reentry SetFirstChild(nsnull); // XXX: no children until the button is menu button. Probably it's not // totally correct but in general AT wants to have leaf buttons. nsCOMPtr content(do_QueryInterface(mDOMNode)); PRBool isMenu = content->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type, nsAccessibilityAtoms::menu, eCaseMatters); PRBool isMenuButton = isMenu ? PR_FALSE : content->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type, nsAccessibilityAtoms::menuButton, eCaseMatters); if (!isMenu && !isMenuButton) return; nsCOMPtr buttonAccessible; nsCOMPtr menupopupAccessible; nsAccessibleTreeWalker walker(mWeakShell, mDOMNode, PR_TRUE); walker.GetFirstChild(); while (walker.mState.accessible) { PRUint32 role = nsAccUtils::Role(walker.mState.accessible); if (role == nsIAccessibleRole::ROLE_MENUPOPUP) { // Get an accessbile for menupopup or panel elements. menupopupAccessible = walker.mState.accessible; } else if (isMenuButton && role == nsIAccessibleRole::ROLE_PUSHBUTTON) { // Button type="menu-button" contains a real button. Get an accessible // for it. Ignore dropmarker button what is placed as a last child. buttonAccessible = walker.mState.accessible; break; } walker.GetNextSibling(); } if (!menupopupAccessible) return; SetFirstChild(menupopupAccessible); nsRefPtr menupopupAcc = nsAccUtils::QueryObject(menupopupAccessible); menupopupAcc->SetParent(this); mAccChildCount++; if (buttonAccessible) { if (menupopupAcc) menupopupAcc->SetNextSibling(buttonAccessible); else SetFirstChild(buttonAccessible); nsRefPtr buttonAcc = nsAccUtils::QueryObject(buttonAccessible); buttonAcc->SetParent(this); mAccChildCount++; } } } //////////////////////////////////////////////////////////////////////////////// // nsXULButtonAccessible protected PRBool nsXULButtonAccessible::ContainsMenu() { static nsIContent::AttrValuesArray strings[] = {&nsAccessibilityAtoms::menu, &nsAccessibilityAtoms::menuButton, nsnull}; nsCOMPtr content(do_QueryInterface(mDOMNode)); return content->FindAttrValueIn(kNameSpaceID_None, nsAccessibilityAtoms::type, strings, eCaseMatters) >= 0; } //////////////////////////////////////////////////////////////////////////////// // nsXULDropmarkerAccessible //////////////////////////////////////////////////////////////////////////////// /** * Default Constructor */ nsXULDropmarkerAccessible::nsXULDropmarkerAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell): nsFormControlAccessible(aNode, aShell) { } /** * Only one action available */ NS_IMETHODIMP nsXULDropmarkerAccessible::GetNumActions(PRUint8 *aResult) { *aResult = 1; return NS_OK; } PRBool nsXULDropmarkerAccessible::DropmarkerOpen(PRBool aToggleOpen) { PRBool isOpen = PR_FALSE; nsCOMPtr parentButtonNode; mDOMNode->GetParentNode(getter_AddRefs(parentButtonNode)); nsCOMPtr parentButtonElement(do_QueryInterface(parentButtonNode)); if (parentButtonElement) { parentButtonElement->GetOpen(&isOpen); if (aToggleOpen) parentButtonElement->SetOpen(!isOpen); } else { nsCOMPtr parentMenuListElement(do_QueryInterface(parentButtonNode)); if (parentMenuListElement) { parentMenuListElement->GetOpen(&isOpen); if (aToggleOpen) parentMenuListElement->SetOpen(!isOpen); } } return isOpen; } /** * Return the name of our only action */ NS_IMETHODIMP nsXULDropmarkerAccessible::GetActionName(PRUint8 aIndex, nsAString& aName) { if (aIndex == eAction_Click) { if (DropmarkerOpen(PR_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 nsXULDropmarkerAccessible::DoAction(PRUint8 index) { if (index == eAction_Click) { DropmarkerOpen(PR_TRUE); // Reverse the open attribute return NS_OK; } return NS_ERROR_INVALID_ARG; } /** * We are a pushbutton */ nsresult nsXULDropmarkerAccessible::GetRoleInternal(PRUint32 *aRole) { *aRole = nsIAccessibleRole::ROLE_PUSHBUTTON; return NS_OK; } nsresult nsXULDropmarkerAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { *aState = 0; if (IsDefunct()) { if (aExtraState) *aExtraState = nsIAccessibleStates::EXT_STATE_DEFUNCT; return NS_OK_DEFUNCT_OBJECT; } if (aExtraState) *aExtraState = 0; if (DropmarkerOpen(PR_FALSE)) *aState = nsIAccessibleStates::STATE_PRESSED; return NS_OK; } /** * XUL checkbox */ /** * Default Constructor */ nsXULCheckboxAccessible::nsXULCheckboxAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell): nsFormControlAccessible(aNode, aShell) { } /** * We are a CheckButton */ nsresult nsXULCheckboxAccessible::GetRoleInternal(PRUint32 *aRole) { *aRole = nsIAccessibleRole::ROLE_CHECKBUTTON; return NS_OK; } /** * Only one action available */ NS_IMETHODIMP nsXULCheckboxAccessible::GetNumActions(PRUint8 *_retval) { *_retval = 1; return NS_OK; } /** * Return the name of our only action */ NS_IMETHODIMP nsXULCheckboxAccessible::GetActionName(PRUint8 aIndex, nsAString& aName) { if (aIndex == eAction_Click) { // check or uncheck PRUint32 state; nsresult rv = GetStateInternal(&state, nsnull); NS_ENSURE_SUCCESS(rv, rv); if (state & nsIAccessibleStates::STATE_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 nsXULCheckboxAccessible::DoAction(PRUint8 index) { if (index == eAction_Click) { return DoCommand(); } return NS_ERROR_INVALID_ARG; } /** * Possible states: focused, focusable, unavailable(disabled), checked */ nsresult nsXULCheckboxAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { // Get focus and disable status from base class nsresult rv = nsFormControlAccessible::GetStateInternal(aState, aExtraState); NS_ENSURE_A11Y_SUCCESS(rv, rv); *aState |= nsIAccessibleStates::STATE_CHECKABLE; // Determine Checked state nsCOMPtr xulCheckboxElement(do_QueryInterface(mDOMNode)); if (xulCheckboxElement) { PRBool checked = PR_FALSE; xulCheckboxElement->GetChecked(&checked); if (checked) { *aState |= nsIAccessibleStates::STATE_CHECKED; PRInt32 checkState = 0; xulCheckboxElement->GetCheckState(&checkState); if (checkState == nsIDOMXULCheckboxElement::CHECKSTATE_MIXED) *aState |= nsIAccessibleStates::STATE_MIXED; } } return NS_OK; } /** * XUL groupbox */ nsXULGroupboxAccessible::nsXULGroupboxAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell): nsAccessibleWrap(aNode, aShell) { } nsresult nsXULGroupboxAccessible::GetRoleInternal(PRUint32 *aRole) { *aRole = nsIAccessibleRole::ROLE_GROUPING; return NS_OK; } nsresult nsXULGroupboxAccessible::GetNameInternal(nsAString& aName) { // XXX: we use the first related accessible only. nsCOMPtr label = nsRelUtils::GetRelatedAccessible(this, nsIAccessibleRelation::RELATION_LABELLED_BY); if (label) { return label->GetName(aName); } return NS_OK; } NS_IMETHODIMP nsXULGroupboxAccessible::GetRelationByType(PRUint32 aRelationType, nsIAccessibleRelation **aRelation) { nsresult rv = nsAccessibleWrap::GetRelationByType(aRelationType, aRelation); NS_ENSURE_SUCCESS(rv, rv); if (aRelationType == nsIAccessibleRelation::RELATION_LABELLED_BY) { // 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 nsCOMPtr testLabelAccessible; while (NextChild(testLabelAccessible)) { if (nsAccUtils::Role(testLabelAccessible) == nsIAccessibleRole::ROLE_LABEL) { // Ensure that it's our label // XXX: we'll fail if group accessible expose more than one relation // targets. nsCOMPtr testGroupboxAccessible = nsRelUtils::GetRelatedAccessible(testLabelAccessible, nsIAccessibleRelation::RELATION_LABEL_FOR); if (testGroupboxAccessible == this) { // The