/* -*- 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 "DocAccessible.h" #include "nsObjCExceptions.h" #include "Accessible-inl.h" #include "nsAccUtils.h" #include "Role.h" #import "mozAccessible.h" #import "mozActionElements.h" #import "mozHTMLAccessible.h" #import "mozTextAccessible.h" using namespace mozilla; using namespace mozilla::a11y; AccessibleWrap:: AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : Accessible(aContent, aDoc), mNativeObject(nil), mNativeInited(false) { } AccessibleWrap::~AccessibleWrap() { } mozAccessible* AccessibleWrap::GetNativeObject() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; if (!mNativeInited && !mNativeObject && !IsDefunct() && !AncestorIsFlat()) { uintptr_t accWrap = reinterpret_cast(this); mNativeObject = [[GetNativeType() alloc] initWithAccessible:accWrap]; } mNativeInited = true; return mNativeObject; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } void AccessibleWrap::GetNativeInterface(void** aOutInterface) { *aOutInterface = static_cast(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_ABORT_BLOCK_NIL; if (IsXULTabpanels()) return [mozPaneAccessible class]; return GetTypeFromRole(Role()); NS_OBJC_END_TRY_ABORT_BLOCK_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; } Accessible::Shutdown(); } nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; nsresult rv = Accessible::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); uint32_t eventType = aEvent->GetEventType(); // ignore everything but focus-changed, value-changed, caret and selection // events for now. if (eventType != nsIAccessibleEvent::EVENT_FOCUS && eventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE && eventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED && eventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) return NS_OK; Accessible* accessible = aEvent->GetAccessible(); NS_ENSURE_STATE(accessible); mozAccessible *nativeAcc = nil; accessible->GetNativeInterface((void**)&nativeAcc); if (!nativeAcc) return NS_ERROR_FAILURE; FireNativeEvent(nativeAcc, eventType); return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } void AccessibleWrap::InvalidateChildren() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; [GetNativeObject() invalidateChildren]; Accessible::InvalidateChildren(); NS_OBJC_END_TRY_ABORT_BLOCK; } bool AccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aAccessible) { bool inserted = Accessible::InsertChildAt(aIdx, aAccessible); if (inserted && mNativeObject) [mNativeObject appendChild:aAccessible]; return inserted; } bool AccessibleWrap::RemoveChild(Accessible* aAccessible) { bool removed = Accessible::RemoveChild(aAccessible); if (removed && mNativeObject) [mNativeObject invalidateChildren]; return removed; } // if we for some reason have no native accessible, we should be skipped over (and traversed) // when fetching all unignored children, etc. when counting unignored children, we will not be counted. bool AccessibleWrap::IsIgnored() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; mozAccessible* nativeObject = GetNativeObject(); return (!nativeObject) || [nativeObject accessibilityIsIgnored]; NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); } void AccessibleWrap::GetUnignoredChildren(nsTArray* aChildrenArray) { // we're flat; there are no children. if (nsAccUtils::MustPrune(this)) return; uint32_t childCount = ChildCount(); for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { AccessibleWrap* childAcc = static_cast(GetChildAt(childIdx)); // If element is ignored, then add its children as substitutes. if (childAcc->IsIgnored()) { childAcc->GetUnignoredChildren(aChildrenArray); continue; } aChildrenArray->AppendElement(childAcc); } } Accessible* AccessibleWrap::GetUnignoredParent() const { // Go up the chain to find a parent that is not ignored. AccessibleWrap* parentWrap = static_cast(Parent()); while (parentWrap && parentWrap->IsIgnored()) parentWrap = static_cast(parentWrap->Parent()); return parentWrap; } //////////////////////////////////////////////////////////////////////////////// // AccessibleWrap protected bool AccessibleWrap::AncestorIsFlat() { // We don't create a native object if we're child of a "flat" accessible; // for example, on OS X buttons shouldn't have any children, because that // makes the OS confused. // // 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. Accessible* parent = Parent(); while (parent) { if (nsAccUtils::MustPrune(parent)) return true; parent = parent->Parent(); } // no parent was flat return false; } void a11y::FireNativeEvent(mozAccessible* aNativeAcc, uint32_t aEventType) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; switch (aEventType) { case nsIAccessibleEvent::EVENT_FOCUS: [aNativeAcc didReceiveFocus]; break; case nsIAccessibleEvent::EVENT_VALUE_CHANGE: [aNativeAcc valueDidChange]; break; case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: [aNativeAcc selectedTextDidChange]; break; } NS_OBJC_END_TRY_ABORT_BLOCK; } Class a11y::GetTypeFromRole(roles::Role aRole) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; switch (aRole) { case roles::COMBOBOX: case roles::PUSHBUTTON: case roles::SPLITBUTTON: case roles::TOGGLE_BUTTON: { return [mozButtonAccessible class]; } case roles::PAGETAB: return [mozButtonAccessible class]; case roles::CHECKBUTTON: return [mozCheckboxAccessible class]; case roles::HEADING: return [mozHeadingAccessible class]; case roles::PAGETABLIST: return [mozTabsAccessible class]; case roles::ENTRY: case roles::STATICTEXT: case roles::CAPTION: case roles::ACCEL_LABEL: case roles::PASSWORD_TEXT: // normal textfield (static or editable) return [mozTextAccessible class]; case roles::TEXT_LEAF: return [mozTextLeafAccessible class]; case roles::LINK: return [mozLinkAccessible class]; default: return [mozAccessible class]; } return nil; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; }