michael@0: /* -*- Mode: C++; tab-width: 2; 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 "DocAccessible.h" michael@0: #include "nsObjCExceptions.h" michael@0: michael@0: #include "Accessible-inl.h" michael@0: #include "nsAccUtils.h" michael@0: #include "Role.h" michael@0: michael@0: #import "mozAccessible.h" michael@0: #import "mozActionElements.h" michael@0: #import "mozHTMLAccessible.h" michael@0: #import "mozTextAccessible.h" michael@0: michael@0: using namespace mozilla::a11y; michael@0: michael@0: AccessibleWrap:: michael@0: AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : michael@0: Accessible(aContent, aDoc), mNativeObject(nil), michael@0: mNativeInited(false) michael@0: { michael@0: } michael@0: michael@0: AccessibleWrap::~AccessibleWrap() michael@0: { michael@0: } michael@0: michael@0: mozAccessible* michael@0: AccessibleWrap::GetNativeObject() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (!mNativeInited && !mNativeObject && !IsDefunct() && !AncestorIsFlat()) michael@0: mNativeObject = [[GetNativeType() alloc] initWithAccessible:this]; michael@0: michael@0: mNativeInited = true; michael@0: michael@0: return mNativeObject; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AccessibleWrap::GetNativeInterface (void **aOutInterface) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOutInterface); michael@0: michael@0: *aOutInterface = static_cast(GetNativeObject()); michael@0: michael@0: return *aOutInterface ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // overridden in subclasses to create the right kind of object. by default we create a generic michael@0: // 'mozAccessible' node. michael@0: Class michael@0: AccessibleWrap::GetNativeType () michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (IsXULTabpanels()) michael@0: return [mozPaneAccessible class]; michael@0: michael@0: roles::Role role = Role(); michael@0: switch (role) { michael@0: case roles::PUSHBUTTON: michael@0: case roles::SPLITBUTTON: michael@0: case roles::TOGGLE_BUTTON: michael@0: { michael@0: // if this button may show a popup, let's make it of the popupbutton type. michael@0: return HasPopup() ? [mozPopupButtonAccessible class] : michael@0: [mozButtonAccessible class]; michael@0: } michael@0: michael@0: case roles::PAGETAB: michael@0: return [mozButtonAccessible class]; michael@0: michael@0: case roles::CHECKBUTTON: michael@0: return [mozCheckboxAccessible class]; michael@0: michael@0: case roles::HEADING: michael@0: return [mozHeadingAccessible class]; michael@0: michael@0: case roles::PAGETABLIST: michael@0: return [mozTabsAccessible class]; michael@0: michael@0: case roles::ENTRY: michael@0: case roles::STATICTEXT: michael@0: case roles::CAPTION: michael@0: case roles::ACCEL_LABEL: michael@0: case roles::PASSWORD_TEXT: michael@0: // normal textfield (static or editable) michael@0: return [mozTextAccessible class]; michael@0: michael@0: case roles::TEXT_LEAF: michael@0: return [mozTextLeafAccessible class]; michael@0: michael@0: case roles::LINK: michael@0: return [mozLinkAccessible class]; michael@0: michael@0: case roles::COMBOBOX: michael@0: return [mozPopupButtonAccessible class]; michael@0: michael@0: default: michael@0: return [mozAccessible class]; michael@0: } michael@0: michael@0: return nil; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: // this method is very important. it is fired when an accessible object "dies". after this point michael@0: // the object might still be around (because some 3rd party still has a ref to it), but it is michael@0: // in fact 'dead'. michael@0: void michael@0: AccessibleWrap::Shutdown () michael@0: { michael@0: // this ensure we will not try to re-create the native object. michael@0: mNativeInited = true; michael@0: michael@0: // we really intend to access the member directly. michael@0: if (mNativeObject) { michael@0: [mNativeObject expire]; michael@0: [mNativeObject release]; michael@0: mNativeObject = nil; michael@0: } michael@0: michael@0: Accessible::Shutdown(); michael@0: } michael@0: michael@0: nsresult michael@0: AccessibleWrap::HandleAccEvent(AccEvent* aEvent) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: nsresult rv = Accessible::HandleAccEvent(aEvent); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t eventType = aEvent->GetEventType(); michael@0: michael@0: // ignore everything but focus-changed, value-changed, caret and selection michael@0: // events for now. michael@0: if (eventType != nsIAccessibleEvent::EVENT_FOCUS && michael@0: eventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE && michael@0: eventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED && michael@0: eventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) michael@0: return NS_OK; michael@0: michael@0: Accessible* accessible = aEvent->GetAccessible(); michael@0: NS_ENSURE_STATE(accessible); michael@0: michael@0: mozAccessible *nativeAcc = nil; michael@0: accessible->GetNativeInterface((void**)&nativeAcc); michael@0: if (!nativeAcc) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: switch (eventType) { michael@0: case nsIAccessibleEvent::EVENT_FOCUS: michael@0: [nativeAcc didReceiveFocus]; michael@0: break; michael@0: case nsIAccessibleEvent::EVENT_VALUE_CHANGE: michael@0: [nativeAcc valueDidChange]; michael@0: break; michael@0: case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: michael@0: case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: michael@0: [nativeAcc selectedTextDidChange]; michael@0: break; michael@0: } michael@0: michael@0: return NS_OK; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: void michael@0: AccessibleWrap::InvalidateChildren() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [GetNativeObject() invalidateChildren]; michael@0: michael@0: Accessible::InvalidateChildren(); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: bool michael@0: AccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aAccessible) michael@0: { michael@0: bool inserted = Accessible::InsertChildAt(aIdx, aAccessible); michael@0: if (inserted && mNativeObject) michael@0: [mNativeObject appendChild:aAccessible]; michael@0: michael@0: return inserted; michael@0: } michael@0: michael@0: bool michael@0: AccessibleWrap::RemoveChild(Accessible* aAccessible) michael@0: { michael@0: bool removed = Accessible::RemoveChild(aAccessible); michael@0: michael@0: if (removed && mNativeObject) michael@0: [mNativeObject invalidateChildren]; michael@0: michael@0: return removed; michael@0: } michael@0: michael@0: // if we for some reason have no native accessible, we should be skipped over (and traversed) michael@0: // when fetching all unignored children, etc. when counting unignored children, we will not be counted. michael@0: bool michael@0: AccessibleWrap::IsIgnored() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: michael@0: mozAccessible* nativeObject = GetNativeObject(); michael@0: return (!nativeObject) || [nativeObject accessibilityIsIgnored]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); michael@0: } michael@0: michael@0: void michael@0: AccessibleWrap::GetUnignoredChildren(nsTArray* aChildrenArray) michael@0: { michael@0: // we're flat; there are no children. michael@0: if (nsAccUtils::MustPrune(this)) michael@0: return; michael@0: michael@0: uint32_t childCount = ChildCount(); michael@0: for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { michael@0: AccessibleWrap* childAcc = michael@0: static_cast(GetChildAt(childIdx)); michael@0: michael@0: // If element is ignored, then add its children as substitutes. michael@0: if (childAcc->IsIgnored()) { michael@0: childAcc->GetUnignoredChildren(aChildrenArray); michael@0: continue; michael@0: } michael@0: michael@0: aChildrenArray->AppendElement(childAcc); michael@0: } michael@0: } michael@0: michael@0: Accessible* michael@0: AccessibleWrap::GetUnignoredParent() const michael@0: { michael@0: // Go up the chain to find a parent that is not ignored. michael@0: AccessibleWrap* parentWrap = static_cast(Parent()); michael@0: while (parentWrap && parentWrap->IsIgnored()) michael@0: parentWrap = static_cast(parentWrap->Parent()); michael@0: michael@0: return parentWrap; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // AccessibleWrap protected michael@0: michael@0: bool michael@0: AccessibleWrap::AncestorIsFlat() michael@0: { michael@0: // We don't create a native object if we're child of a "flat" accessible; michael@0: // for example, on OS X buttons shouldn't have any children, because that michael@0: // makes the OS confused. michael@0: // michael@0: // To maintain a scripting environment where the XPCOM accessible hierarchy michael@0: // look the same on all platforms, we still let the C++ objects be created michael@0: // though. michael@0: michael@0: Accessible* parent = Parent(); michael@0: while (parent) { michael@0: if (nsAccUtils::MustPrune(parent)) michael@0: return true; michael@0: michael@0: parent = parent->Parent(); michael@0: } michael@0: // no parent was flat michael@0: return false; michael@0: }