diff options
Diffstat (limited to '')
-rw-r--r-- | accessible/mac/AccessibleWrap.mm | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/accessible/mac/AccessibleWrap.mm b/accessible/mac/AccessibleWrap.mm new file mode 100644 index 0000000000..ef2f4ba779 --- /dev/null +++ b/accessible/mac/AccessibleWrap.mm @@ -0,0 +1,281 @@ +/* clang-format off */ +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* clang-format on */ +/* 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 "DocAccessibleWrap.h" +#include "nsObjCExceptions.h" +#include "nsCocoaUtils.h" +#include "nsUnicharUtils.h" + +#include "LocalAccessible-inl.h" +#include "nsAccUtils.h" +#include "mozilla/a11y/Role.h" +#include "TextRange.h" +#include "gfxPlatform.h" + +#import "MOXLandmarkAccessibles.h" +#import "MOXMathAccessibles.h" +#import "MOXTextMarkerDelegate.h" +#import "MOXWebAreaAccessible.h" +#import "mozAccessible.h" +#import "mozActionElements.h" +#import "mozHTMLAccessible.h" +#import "mozSelectableElements.h" +#import "mozTableAccessible.h" +#import "mozTextAccessible.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) + : LocalAccessible(aContent, aDoc), + mNativeObject(nil), + mNativeInited(false) { + if (aContent && aContent->IsElement() && aDoc) { + // Check if this accessible is a live region and queue it + // it for dispatching an event after it has been inserted. + DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(aDoc); + static const dom::Element::AttrValuesArray sLiveRegionValues[] = { + nsGkAtoms::OFF, nsGkAtoms::polite, nsGkAtoms::assertive, nullptr}; + int32_t attrValue = nsAccUtils::FindARIAAttrValueIn( + aContent->AsElement(), nsGkAtoms::aria_live, sLiveRegionValues, + eIgnoreCase); + if (attrValue == 0) { + // aria-live is "off", do nothing. + } else if (attrValue > 0) { + // aria-live attribute is polite or assertive. It's live! + doc->QueueNewLiveRegion(this); + } else if (const nsRoleMapEntry* roleMap = + aria::GetRoleMap(aContent->AsElement())) { + // aria role defines it as a live region. It's live! + if (roleMap->liveAttRule == ePoliteLiveAttr || + roleMap->liveAttRule == eAssertiveLiveAttr) { + doc->QueueNewLiveRegion(this); + } + } else if (nsStaticAtom* value = GetAccService()->MarkupAttribute( + aContent, nsGkAtoms::aria_live)) { + // HTML element defines it as a live region. It's live! + if (value == nsGkAtoms::polite || value == nsGkAtoms::assertive) { + doc->QueueNewLiveRegion(this); + } + } + } +} + +AccessibleWrap::~AccessibleWrap() {} + +mozAccessible* AccessibleWrap::GetNativeObject() { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + + if (!mNativeInited && !mNativeObject) { + // We don't creat OSX accessibles for xul tooltips, defunct accessibles, + // <br> (whitespace) elements, or pruned children. + // + // To maintain a scripting environment where the XPCOM accessible hierarchy + // look the same on all platforms, we still let the C++ objects be created + // though. + if (!IsXULTooltip() && !IsDefunct() && Role() != roles::WHITESPACE) { + mNativeObject = [[GetNativeType() alloc] initWithAccessible:this]; + } + } + + mNativeInited = true; + + return mNativeObject; + + NS_OBJC_END_TRY_BLOCK_RETURN(nil); +} + +void AccessibleWrap::GetNativeInterface(void** aOutInterface) { + *aOutInterface = static_cast<void*>(GetNativeObject()); +} + +// overridden in subclasses to create the right kind of object. by default we +// create a generic 'mozAccessible' node. +Class AccessibleWrap::GetNativeType() { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + + if (IsXULTabpanels()) { + return [mozPaneAccessible class]; + } + + if (IsTable()) { + return [mozTableAccessible class]; + } + + if (IsTableRow()) { + return [mozTableRowAccessible class]; + } + + if (IsTableCell()) { + return [mozTableCellAccessible class]; + } + + if (IsDoc()) { + return [MOXWebAreaAccessible class]; + } + + return GetTypeFromRole(Role()); + + NS_OBJC_END_TRY_BLOCK_RETURN(nil); +} + +// this method is very important. it is fired when an accessible object "dies". +// after this point the object might still be around (because some 3rd party +// still has a ref to it), but it is in fact 'dead'. +void AccessibleWrap::Shutdown() { + // this ensure we will not try to re-create the native object. + mNativeInited = true; + + // we really intend to access the member directly. + if (mNativeObject) { + [mNativeObject expire]; + [mNativeObject release]; + mNativeObject = nil; + } + + LocalAccessible::Shutdown(); +} + +nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + + nsresult rv = LocalAccessible::HandleAccEvent(aEvent); + NS_ENSURE_SUCCESS(rv, rv); + + if (IsDefunct()) { + // The accessible can become defunct after their events are handled. + return NS_OK; + } + + uint32_t eventType = aEvent->GetEventType(); + + if (eventType == nsIAccessibleEvent::EVENT_SHOW) { + DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(Document()); + doc->ProcessNewLiveRegions(); + } + + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); +} + +//////////////////////////////////////////////////////////////////////////////// +// AccessibleWrap protected + +Class a11y::GetTypeFromRole(roles::Role aRole) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + + switch (aRole) { + case roles::COMBOBOX: + return [mozPopupButtonAccessible class]; + + case roles::PUSHBUTTON: + return [mozButtonAccessible class]; + + case roles::PAGETAB: + return [mozTabAccessible class]; + + case roles::DATE_EDITOR: + return [mozDatePickerAccessible class]; + + case roles::CHECKBUTTON: + case roles::TOGGLE_BUTTON: + case roles::SWITCH: + case roles::CHECK_MENU_ITEM: + return [mozCheckboxAccessible class]; + + case roles::RADIOBUTTON: + case roles::RADIO_MENU_ITEM: + return [mozRadioButtonAccessible class]; + + case roles::SPINBUTTON: + case roles::SLIDER: + return [mozIncrementableAccessible class]; + + case roles::HEADING: + return [mozHeadingAccessible class]; + + case roles::PAGETABLIST: + return [mozTabGroupAccessible class]; + + case roles::ENTRY: + case roles::CAPTION: + case roles::EDITCOMBOBOX: + case roles::PASSWORD_TEXT: + // normal textfield (static or editable) + return [mozTextAccessible class]; + + case roles::TEXT_LEAF: + case roles::STATICTEXT: + return [mozTextLeafAccessible class]; + + case roles::LANDMARK: + return [MOXLandmarkAccessible class]; + + case roles::LINK: + return [mozLinkAccessible class]; + + case roles::LISTBOX: + return [mozListboxAccessible class]; + + case roles::LISTITEM: + return [MOXListItemAccessible class]; + + case roles::OPTION: { + return [mozOptionAccessible class]; + } + + case roles::RICH_OPTION: { + return [mozSelectableChildAccessible class]; + } + + case roles::COMBOBOX_LIST: + case roles::MENUBAR: + case roles::MENUPOPUP: { + return [mozMenuAccessible class]; + } + + case roles::COMBOBOX_OPTION: + case roles::PARENT_MENUITEM: + case roles::MENUITEM: { + return [mozMenuItemAccessible class]; + } + + case roles::MATHML_ROOT: + return [MOXMathRootAccessible class]; + + case roles::MATHML_SQUARE_ROOT: + return [MOXMathSquareRootAccessible class]; + + case roles::MATHML_FRACTION: + return [MOXMathFractionAccessible class]; + + case roles::MATHML_SUB: + case roles::MATHML_SUP: + case roles::MATHML_SUB_SUP: + return [MOXMathSubSupAccessible class]; + + case roles::MATHML_UNDER: + case roles::MATHML_OVER: + case roles::MATHML_UNDER_OVER: + return [MOXMathUnderOverAccessible class]; + + case roles::OUTLINE: + case roles::TREE_TABLE: + return [mozOutlineAccessible class]; + + case roles::OUTLINEITEM: + return [mozOutlineRowAccessible class]; + + default: + return [mozAccessible class]; + } + + return nil; + + NS_OBJC_END_TRY_BLOCK_RETURN(nil); +} |