michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "XULComboboxAccessible.h" michael@0: michael@0: #include "Accessible-inl.h" michael@0: #include "nsAccessibilityService.h" michael@0: #include "DocAccessible.h" michael@0: #include "nsCoreUtils.h" michael@0: #include "Role.h" michael@0: #include "States.h" michael@0: michael@0: #include "nsIAutoCompleteInput.h" michael@0: #include "nsIDOMXULMenuListElement.h" michael@0: #include "nsIDOMXULSelectCntrlItemEl.h" michael@0: michael@0: using namespace mozilla::a11y; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULComboboxAccessible michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: XULComboboxAccessible:: michael@0: XULComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) : michael@0: AccessibleWrap(aContent, aDoc) michael@0: { michael@0: if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, michael@0: nsGkAtoms::autocomplete, eIgnoreCase)) michael@0: mGenericTypes |= eAutoComplete; michael@0: else michael@0: mGenericTypes |= eCombobox; michael@0: } michael@0: michael@0: role michael@0: XULComboboxAccessible::NativeRole() michael@0: { michael@0: return IsAutoComplete() ? roles::AUTOCOMPLETE : roles::COMBOBOX; michael@0: } michael@0: michael@0: uint64_t michael@0: XULComboboxAccessible::NativeState() michael@0: { michael@0: // As a nsComboboxAccessible we can have the following states: michael@0: // STATE_FOCUSED michael@0: // STATE_FOCUSABLE michael@0: // STATE_HASPOPUP michael@0: // STATE_EXPANDED michael@0: // STATE_COLLAPSED michael@0: michael@0: // Get focus status from base class michael@0: uint64_t state = Accessible::NativeState(); michael@0: michael@0: nsCOMPtr menuList(do_QueryInterface(mContent)); michael@0: if (menuList) { michael@0: bool isOpen = false; michael@0: menuList->GetOpen(&isOpen); michael@0: if (isOpen) michael@0: state |= states::EXPANDED; michael@0: else michael@0: state |= states::COLLAPSED; michael@0: } michael@0: michael@0: return state | states::HASPOPUP; michael@0: } michael@0: michael@0: void michael@0: XULComboboxAccessible::Description(nsString& aDescription) michael@0: { michael@0: aDescription.Truncate(); michael@0: // Use description of currently focused option michael@0: nsCOMPtr menuListElm(do_QueryInterface(mContent)); michael@0: if (!menuListElm) michael@0: return; michael@0: michael@0: nsCOMPtr focusedOptionItem; michael@0: menuListElm->GetSelectedItem(getter_AddRefs(focusedOptionItem)); michael@0: nsCOMPtr focusedOptionContent = michael@0: do_QueryInterface(focusedOptionItem); michael@0: if (focusedOptionContent && mDoc) { michael@0: Accessible* focusedOptionAcc = mDoc->GetAccessible(focusedOptionContent); michael@0: if (focusedOptionAcc) michael@0: focusedOptionAcc->Description(aDescription); michael@0: } michael@0: } michael@0: michael@0: void michael@0: XULComboboxAccessible::Value(nsString& aValue) michael@0: { michael@0: aValue.Truncate(); michael@0: michael@0: // The value is the option or text shown entered in the combobox. michael@0: nsCOMPtr menuList(do_QueryInterface(mContent)); michael@0: if (menuList) michael@0: menuList->GetLabel(aValue); michael@0: } michael@0: michael@0: bool michael@0: XULComboboxAccessible::CanHaveAnonChildren() michael@0: { michael@0: if (mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) || michael@0: mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable, michael@0: nsGkAtoms::_true, eIgnoreCase)) { michael@0: // Both the XUL and widgets michael@0: // use XULComboboxAccessible. We need to walk the anonymous children for these michael@0: // so that the entry field is a child michael@0: return true; michael@0: } michael@0: michael@0: // Argument of false indicates we don't walk anonymous children for michael@0: // menuitems michael@0: return false; michael@0: } michael@0: michael@0: uint8_t michael@0: XULComboboxAccessible::ActionCount() michael@0: { michael@0: // Just one action (click). michael@0: return 1; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULComboboxAccessible::DoAction(uint8_t aIndex) michael@0: { michael@0: if (aIndex != XULComboboxAccessible::eAction_Click) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (IsDefunct()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Programmaticaly toggle the combo box. michael@0: nsCOMPtr menuList(do_QueryInterface(mContent)); michael@0: if (!menuList) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: bool isDroppedDown; michael@0: menuList->GetOpen(&isDroppedDown); michael@0: return menuList->SetOpen(!isDroppedDown); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULComboboxAccessible::GetActionName(uint8_t aIndex, nsAString& aName) michael@0: { michael@0: if (aIndex != XULComboboxAccessible::eAction_Click) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (IsDefunct()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Our action name is the reverse of our state: michael@0: // if we are close -> open is our name. michael@0: // if we are open -> close is our name. michael@0: // Uses the frame to get the state, updated on every click. michael@0: michael@0: nsCOMPtr menuList(do_QueryInterface(mContent)); michael@0: if (!menuList) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: bool isDroppedDown; michael@0: menuList->GetOpen(&isDroppedDown); michael@0: if (isDroppedDown) michael@0: aName.AssignLiteral("close"); michael@0: else michael@0: aName.AssignLiteral("open"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Widgets michael@0: michael@0: bool michael@0: XULComboboxAccessible::IsActiveWidget() const michael@0: { michael@0: if (IsAutoComplete() || michael@0: mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable, michael@0: nsGkAtoms::_true, eIgnoreCase)) { michael@0: int32_t childCount = mChildren.Length(); michael@0: for (int32_t idx = 0; idx < childCount; idx++) { michael@0: Accessible* child = mChildren[idx]; michael@0: if (child->Role() == roles::ENTRY) michael@0: return FocusMgr()->HasDOMFocus(child->GetContent()); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: return FocusMgr()->HasDOMFocus(mContent); michael@0: } michael@0: michael@0: bool michael@0: XULComboboxAccessible::AreItemsOperable() const michael@0: { michael@0: if (IsAutoComplete()) { michael@0: nsCOMPtr autoCompleteInputElm = michael@0: do_QueryInterface(mContent); michael@0: if (autoCompleteInputElm) { michael@0: bool isOpen = false; michael@0: autoCompleteInputElm->GetPopupOpen(&isOpen); michael@0: return isOpen; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr menuListElm = do_QueryInterface(mContent); michael@0: if (menuListElm) { michael@0: bool isOpen = false; michael@0: menuListElm->GetOpen(&isOpen); michael@0: return isOpen; michael@0: } michael@0: michael@0: return false; michael@0: }