123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- /* -*- 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 "XULMenuAccessible.h"
- #include "Accessible-inl.h"
- #include "nsAccessibilityService.h"
- #include "nsAccUtils.h"
- #include "DocAccessible.h"
- #include "Role.h"
- #include "States.h"
- #include "XULFormControlAccessible.h"
- #include "nsIDOMElement.h"
- #include "nsIDOMXULElement.h"
- #include "nsIMutableArray.h"
- #include "nsIDOMXULContainerElement.h"
- #include "nsIDOMXULSelectCntrlItemEl.h"
- #include "nsIDOMXULMultSelectCntrlEl.h"
- #include "nsIDOMKeyEvent.h"
- #include "nsIServiceManager.h"
- #include "nsIPresShell.h"
- #include "nsIContent.h"
- #include "nsMenuBarFrame.h"
- #include "nsMenuPopupFrame.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/LookAndFeel.h"
- #include "mozilla/dom/Element.h"
- using namespace mozilla;
- using namespace mozilla::a11y;
- ////////////////////////////////////////////////////////////////////////////////
- // XULMenuitemAccessible
- ////////////////////////////////////////////////////////////////////////////////
- XULMenuitemAccessible::
- XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc) :
- AccessibleWrap(aContent, aDoc)
- {
- mStateFlags |= eNoXBLKids;
- }
- uint64_t
- XULMenuitemAccessible::NativeState()
- {
- uint64_t state = Accessible::NativeState();
- // Has Popup?
- if (mContent->NodeInfo()->Equals(nsGkAtoms::menu, kNameSpaceID_XUL)) {
- state |= states::HASPOPUP;
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::open))
- state |= states::EXPANDED;
- else
- state |= states::COLLAPSED;
- }
- // Checkable/checked?
- static nsIContent::AttrValuesArray strings[] =
- { &nsGkAtoms::radio, &nsGkAtoms::checkbox, nullptr };
- if (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, strings,
- eCaseMatters) >= 0) {
- // Checkable?
- state |= states::CHECKABLE;
- // Checked?
- if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
- nsGkAtoms::_true, eCaseMatters))
- state |= states::CHECKED;
- }
- // Combo box listitem
- bool isComboboxOption = (Role() == roles::COMBOBOX_OPTION);
- if (isComboboxOption) {
- // Is selected?
- bool isSelected = false;
- nsCOMPtr<nsIDOMXULSelectControlItemElement>
- item(do_QueryInterface(mContent));
- NS_ENSURE_TRUE(item, state);
- item->GetSelected(&isSelected);
- // Is collapsed?
- bool isCollapsed = false;
- Accessible* parent = Parent();
- if (parent && parent->State() & states::INVISIBLE)
- isCollapsed = true;
- if (isSelected) {
- state |= states::SELECTED;
- // Selected and collapsed?
- if (isCollapsed) {
- // Set selected option offscreen/invisible according to combobox state
- Accessible* grandParent = parent->Parent();
- if (!grandParent)
- return state;
- NS_ASSERTION(grandParent->Role() == roles::COMBOBOX,
- "grandparent of combobox listitem is not combobox");
- uint64_t grandParentState = grandParent->State();
- state &= ~(states::OFFSCREEN | states::INVISIBLE);
- state |= (grandParentState & states::OFFSCREEN) |
- (grandParentState & states::INVISIBLE) |
- (grandParentState & states::OPAQUE1);
- } // isCollapsed
- } // isSelected
- } // ROLE_COMBOBOX_OPTION
- return state;
- }
- uint64_t
- XULMenuitemAccessible::NativeInteractiveState() const
- {
- if (NativelyUnavailable()) {
- // Note: keep in sinc with nsXULPopupManager::IsValidMenuItem() logic.
- bool skipNavigatingDisabledMenuItem = true;
- nsMenuFrame* menuFrame = do_QueryFrame(GetFrame());
- if (!menuFrame || !menuFrame->IsOnMenuBar()) {
- skipNavigatingDisabledMenuItem = LookAndFeel::
- GetInt(LookAndFeel::eIntID_SkipNavigatingDisabledMenuItem, 0) != 0;
- }
- if (skipNavigatingDisabledMenuItem)
- return states::UNAVAILABLE;
- return states::UNAVAILABLE | states::FOCUSABLE | states::SELECTABLE;
- }
- return states::FOCUSABLE | states::SELECTABLE;
- }
- ENameValueFlag
- XULMenuitemAccessible::NativeName(nsString& aName)
- {
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
- return eNameOK;
- }
- void
- XULMenuitemAccessible::Description(nsString& aDescription)
- {
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::description,
- aDescription);
- }
- KeyBinding
- XULMenuitemAccessible::AccessKey() const
- {
- // Return menu accesskey: N or Alt+F.
- static int32_t gMenuAccesskeyModifier = -1; // magic value of -1 indicates unitialized state
- // We do not use nsCoreUtils::GetAccesskeyFor() because accesskeys for
- // menu are't registered by EventStateManager.
- nsAutoString accesskey;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey,
- accesskey);
- if (accesskey.IsEmpty())
- return KeyBinding();
- uint32_t modifierKey = 0;
- Accessible* parentAcc = Parent();
- if (parentAcc) {
- if (parentAcc->NativeRole() == roles::MENUBAR) {
- // If top level menu item, add Alt+ or whatever modifier text to string
- // No need to cache pref service, this happens rarely
- if (gMenuAccesskeyModifier == -1) {
- // Need to initialize cached global accesskey pref
- gMenuAccesskeyModifier = Preferences::GetInt("ui.key.menuAccessKey", 0);
- }
- switch (gMenuAccesskeyModifier) {
- case nsIDOMKeyEvent::DOM_VK_CONTROL:
- modifierKey = KeyBinding::kControl;
- break;
- case nsIDOMKeyEvent::DOM_VK_ALT:
- modifierKey = KeyBinding::kAlt;
- break;
- case nsIDOMKeyEvent::DOM_VK_META:
- modifierKey = KeyBinding::kMeta;
- break;
- case nsIDOMKeyEvent::DOM_VK_WIN:
- modifierKey = KeyBinding::kOS;
- break;
- }
- }
- }
- return KeyBinding(accesskey[0], modifierKey);
- }
- KeyBinding
- XULMenuitemAccessible::KeyboardShortcut() const
- {
- nsAutoString keyElmId;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyElmId);
- if (keyElmId.IsEmpty())
- return KeyBinding();
- nsIContent* keyElm = mContent->OwnerDoc()->GetElementById(keyElmId);
- if (!keyElm)
- return KeyBinding();
- uint32_t key = 0;
- nsAutoString keyStr;
- keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyStr);
- if (keyStr.IsEmpty()) {
- nsAutoString keyCodeStr;
- keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyCodeStr);
- nsresult errorCode;
- key = keyStr.ToInteger(&errorCode, kAutoDetect);
- } else {
- key = keyStr[0];
- }
- nsAutoString modifiersStr;
- keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr);
- uint32_t modifierMask = 0;
- if (modifiersStr.Find("shift") != -1)
- modifierMask |= KeyBinding::kShift;
- if (modifiersStr.Find("alt") != -1)
- modifierMask |= KeyBinding::kAlt;
- if (modifiersStr.Find("meta") != -1)
- modifierMask |= KeyBinding::kMeta;
- if (modifiersStr.Find("os") != -1)
- modifierMask |= KeyBinding::kOS;
- if (modifiersStr.Find("control") != -1)
- modifierMask |= KeyBinding::kControl;
- if (modifiersStr.Find("accel") != -1) {
- modifierMask |= KeyBinding::AccelModifier();
- }
- return KeyBinding(key, modifierMask);
- }
- role
- XULMenuitemAccessible::NativeRole()
- {
- nsCOMPtr<nsIDOMXULContainerElement> xulContainer(do_QueryInterface(mContent));
- if (xulContainer)
- return roles::PARENT_MENUITEM;
- if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
- return roles::COMBOBOX_OPTION;
- if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- nsGkAtoms::radio, eCaseMatters))
- return roles::RADIO_MENU_ITEM;
- if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- nsGkAtoms::checkbox,
- eCaseMatters))
- return roles::CHECK_MENU_ITEM;
- return roles::MENUITEM;
- }
- int32_t
- XULMenuitemAccessible::GetLevelInternal()
- {
- return nsAccUtils::GetLevelForXULContainerItem(mContent);
- }
- bool
- XULMenuitemAccessible::DoAction(uint8_t index)
- {
- if (index == eAction_Click) { // default action
- DoCommand();
- return true;
- }
- return false;
- }
- void
- XULMenuitemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
- {
- if (aIndex == eAction_Click)
- aName.AssignLiteral("click");
- }
- uint8_t
- XULMenuitemAccessible::ActionCount()
- {
- return 1;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // XULMenuitemAccessible: Widgets
- bool
- XULMenuitemAccessible::IsActiveWidget() const
- {
- // Parent menu item is a widget, it's active when its popup is open.
- nsIContent* menuPopupContent = mContent->GetFirstChild();
- if (menuPopupContent) {
- nsMenuPopupFrame* menuPopupFrame =
- do_QueryFrame(menuPopupContent->GetPrimaryFrame());
- return menuPopupFrame && menuPopupFrame->IsOpen();
- }
- return false;
- }
- bool
- XULMenuitemAccessible::AreItemsOperable() const
- {
- // Parent menu item is a widget, its items are operable when its popup is open.
- nsIContent* menuPopupContent = mContent->GetFirstChild();
- if (menuPopupContent) {
- nsMenuPopupFrame* menuPopupFrame =
- do_QueryFrame(menuPopupContent->GetPrimaryFrame());
- return menuPopupFrame && menuPopupFrame->IsOpen();
- }
- return false;
- }
- Accessible*
- XULMenuitemAccessible::ContainerWidget() const
- {
- nsMenuFrame* menuFrame = do_QueryFrame(GetFrame());
- if (menuFrame) {
- nsMenuParent* menuParent = menuFrame->GetMenuParent();
- if (menuParent) {
- if (menuParent->IsMenuBar()) // menubar menu
- return mParent;
- // a menupoup or parent menu item
- if (menuParent->IsMenu())
- return mParent;
- // otherwise it's different kind of popups (like panel or tooltip), it
- // shouldn't be a real case.
- }
- }
- return nullptr;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // XULMenuSeparatorAccessible
- ////////////////////////////////////////////////////////////////////////////////
- XULMenuSeparatorAccessible::
- XULMenuSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc) :
- XULMenuitemAccessible(aContent, aDoc)
- {
- }
- uint64_t
- XULMenuSeparatorAccessible::NativeState()
- {
- // Isn't focusable, but can be offscreen/invisible -- only copy those states
- return XULMenuitemAccessible::NativeState() &
- (states::OFFSCREEN | states::INVISIBLE);
- }
- ENameValueFlag
- XULMenuSeparatorAccessible::NativeName(nsString& aName)
- {
- return eNameOK;
- }
- role
- XULMenuSeparatorAccessible::NativeRole()
- {
- return roles::SEPARATOR;
- }
- bool
- XULMenuSeparatorAccessible::DoAction(uint8_t index)
- {
- return false;
- }
- void
- XULMenuSeparatorAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
- {
- aName.Truncate();
- }
- uint8_t
- XULMenuSeparatorAccessible::ActionCount()
- {
- return 0;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // XULMenupopupAccessible
- ////////////////////////////////////////////////////////////////////////////////
- XULMenupopupAccessible::
- XULMenupopupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
- XULSelectControlAccessible(aContent, aDoc)
- {
- nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
- if (menuPopupFrame && menuPopupFrame->IsMenu())
- mType = eMenuPopupType;
- // May be the anonymous <menupopup> inside <menulist> (a combobox)
- mSelectControl = do_QueryInterface(mContent->GetFlattenedTreeParent());
- if (!mSelectControl)
- mGenericTypes &= ~eSelect;
- mStateFlags |= eNoXBLKids;
- }
- uint64_t
- XULMenupopupAccessible::NativeState()
- {
- uint64_t state = Accessible::NativeState();
- #ifdef DEBUG
- // We are onscreen if our parent is active
- bool isActive = mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::menuactive);
- if (!isActive) {
- Accessible* parent = Parent();
- if (parent) {
- nsIContent* parentContent = parent->GetContent();
- if (parentContent)
- isActive = parentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::open);
- }
- }
- NS_ASSERTION(isActive || (state & states::INVISIBLE),
- "XULMenupopup doesn't have INVISIBLE when it's inactive");
- #endif
- if (state & states::INVISIBLE)
- state |= states::OFFSCREEN | states::COLLAPSED;
- return state;
- }
- ENameValueFlag
- XULMenupopupAccessible::NativeName(nsString& aName)
- {
- nsIContent* content = mContent;
- while (content && aName.IsEmpty()) {
- content->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
- content = content->GetFlattenedTreeParent();
- }
- return eNameOK;
- }
- role
- XULMenupopupAccessible::NativeRole()
- {
- // If accessible is not bound to the tree (this happens while children are
- // cached) return general role.
- if (mParent) {
- roles::Role role = mParent->Role();
- if (role == roles::COMBOBOX || role == roles::AUTOCOMPLETE)
- return roles::COMBOBOX_LIST;
- if (role == roles::PUSHBUTTON) {
- // Some widgets like the search bar have several popups, owned by buttons.
- Accessible* grandParent = mParent->Parent();
- if (grandParent && grandParent->Role() == roles::AUTOCOMPLETE)
- return roles::COMBOBOX_LIST;
- }
- }
- return roles::MENUPOPUP;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // XULMenupopupAccessible: Widgets
- bool
- XULMenupopupAccessible::IsWidget() const
- {
- return true;
- }
- bool
- XULMenupopupAccessible::IsActiveWidget() const
- {
- // If menupopup is a widget (the case of context menus) then active when open.
- nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
- return menuPopupFrame && menuPopupFrame->IsOpen();
- }
- bool
- XULMenupopupAccessible::AreItemsOperable() const
- {
- nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
- return menuPopupFrame && menuPopupFrame->IsOpen();
- }
- Accessible*
- XULMenupopupAccessible::ContainerWidget() const
- {
- DocAccessible* document = Document();
- nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
- while (menuPopupFrame) {
- Accessible* menuPopup =
- document->GetAccessible(menuPopupFrame->GetContent());
- if (!menuPopup) // shouldn't be a real case
- return nullptr;
- nsMenuFrame* menuFrame = do_QueryFrame(menuPopupFrame->GetParent());
- if (!menuFrame) // context menu or popups
- return nullptr;
- nsMenuParent* menuParent = menuFrame->GetMenuParent();
- if (!menuParent) // menulist or menubutton
- return menuPopup->Parent();
- if (menuParent->IsMenuBar()) { // menubar menu
- nsMenuBarFrame* menuBarFrame = static_cast<nsMenuBarFrame*>(menuParent);
- return document->GetAccessible(menuBarFrame->GetContent());
- }
- // different kind of popups like panel or tooltip
- if (!menuParent->IsMenu())
- return nullptr;
- menuPopupFrame = static_cast<nsMenuPopupFrame*>(menuParent);
- }
- NS_NOTREACHED("Shouldn't be a real case.");
- return nullptr;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // XULMenubarAccessible
- ////////////////////////////////////////////////////////////////////////////////
- XULMenubarAccessible::
- XULMenubarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
- AccessibleWrap(aContent, aDoc)
- {
- }
- ENameValueFlag
- XULMenubarAccessible::NativeName(nsString& aName)
- {
- aName.AssignLiteral("Application");
- return eNameOK;
- }
- role
- XULMenubarAccessible::NativeRole()
- {
- return roles::MENUBAR;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // XULMenubarAccessible: Widgets
- bool
- XULMenubarAccessible::IsActiveWidget() const
- {
- nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
- return menuBarFrame && menuBarFrame->IsActive();
- }
- bool
- XULMenubarAccessible::AreItemsOperable() const
- {
- return true;
- }
- Accessible*
- XULMenubarAccessible::CurrentItem()
- {
- nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
- if (menuBarFrame) {
- nsMenuFrame* menuFrame = menuBarFrame->GetCurrentMenuItem();
- if (menuFrame) {
- nsIContent* menuItemNode = menuFrame->GetContent();
- return mDoc->GetAccessible(menuItemNode);
- }
- }
- return nullptr;
- }
- void
- XULMenubarAccessible::SetCurrentItem(Accessible* aItem)
- {
- NS_ERROR("XULMenubarAccessible::SetCurrentItem not implemented");
- }
|