diff -r 000000000000 -r 6474c204b198 accessible/src/generic/Accessible.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/accessible/src/generic/Accessible.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,3337 @@ +/* -*- 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 "Accessible-inl.h" + +#include "nsIXBLAccessible.h" + +#include "AccCollector.h" +#include "AccGroupInfo.h" +#include "AccIterator.h" +#include "nsAccUtils.h" +#include "nsAccessibleRelation.h" +#include "nsAccessibilityService.h" +#include "ApplicationAccessible.h" +#include "nsCoreUtils.h" +#include "nsIAccessibleRelation.h" +#include "nsIAccessibleRole.h" +#include "nsEventShell.h" +#include "nsTextEquivUtils.h" +#include "Relation.h" +#include "Role.h" +#include "RootAccessible.h" +#include "States.h" +#include "StyleInfo.h" +#include "TableAccessible.h" +#include "TableCellAccessible.h" +#include "TreeWalker.h" + +#include "nsIDOMElement.h" +#include "nsIDOMNodeFilter.h" +#include "nsIDOMHTMLElement.h" +#include "nsIDOMKeyEvent.h" +#include "nsIDOMTreeWalker.h" +#include "nsIDOMXULButtonElement.h" +#include "nsIDOMXULDocument.h" +#include "nsIDOMXULElement.h" +#include "nsIDOMXULLabelElement.h" +#include "nsIDOMXULSelectCntrlEl.h" +#include "nsIDOMXULSelectCntrlItemEl.h" +#include "nsPIDOMWindow.h" + +#include "nsIDocument.h" +#include "nsIContent.h" +#include "nsIForm.h" +#include "nsIFormControl.h" + +#include "nsDeckFrame.h" +#include "nsLayoutUtils.h" +#include "nsIPresShell.h" +#include "nsIStringBundle.h" +#include "nsPresContext.h" +#include "nsIFrame.h" +#include "nsView.h" +#include "nsIDocShellTreeItem.h" +#include "nsIScrollableFrame.h" +#include "nsFocusManager.h" + +#include "nsXPIDLString.h" +#include "nsUnicharUtils.h" +#include "nsReadableUtils.h" +#include "prdtoa.h" +#include "nsIAtom.h" +#include "nsIURI.h" +#include "nsArrayUtils.h" +#include "nsIMutableArray.h" +#include "nsIObserverService.h" +#include "nsIServiceManager.h" +#include "nsWhitespaceTokenizer.h" +#include "nsAttrName.h" +#include "nsNetUtil.h" + +#ifdef DEBUG +#include "nsIDOMCharacterData.h" +#endif + +#include "mozilla/Assertions.h" +#include "mozilla/EventStates.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/unused.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/TreeWalker.h" + +using namespace mozilla; +using namespace mozilla::a11y; + + +//////////////////////////////////////////////////////////////////////////////// +// Accessible: nsISupports and cycle collection + +NS_IMPL_CYCLE_COLLECTION(Accessible, + mContent, mParent, mChildren) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Accessible) + NS_INTERFACE_MAP_ENTRY(nsIAccessible) + if (aIID.Equals(NS_GET_IID(Accessible))) + foundInterface = static_cast(this); + else + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleSelectable, IsSelect()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleValue, HasNumericValue()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleHyperLink, IsLink()) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessible) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessible) +NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease()) + +Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) : + mContent(aContent), mDoc(aDoc), + mParent(nullptr), mIndexInParent(-1), mChildrenFlags(eChildrenUninitialized), + mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0), + mIndexOfEmbeddedChild(-1), mRoleMapEntry(nullptr) +{ +#ifdef NS_DEBUG_X + { + nsCOMPtr shell(do_QueryReferent(aShell)); + printf(">>> %p Created Acc - DOM: %p PS: %p", + (void*)static_cast(this), (void*)aNode, + (void*)shell.get()); + nsCOMPtr content = do_QueryInterface(aNode); + if (content) { + printf(" Con: %s@%p", + NS_ConvertUTF16toUTF8(content->NodeInfo()->QualifiedName()).get(), + (void *)content.get()); + nsAutoString buf; + Name(buf); + printf(" Name:[%s]", NS_ConvertUTF16toUTF8(buf).get()); + } + printf("\n"); + } +#endif +} + +Accessible::~Accessible() +{ + NS_ASSERTION(!mDoc, "LastRelease was never called!?!"); +} + +NS_IMETHODIMP +Accessible::GetDocument(nsIAccessibleDocument** aDocument) +{ + NS_ENSURE_ARG_POINTER(aDocument); + + NS_IF_ADDREF(*aDocument = Document()); + return NS_OK; +} + +NS_IMETHODIMP +Accessible::GetDOMNode(nsIDOMNode** aDOMNode) +{ + NS_ENSURE_ARG_POINTER(aDOMNode); + *aDOMNode = nullptr; + + nsINode *node = GetNode(); + if (node) + CallQueryInterface(node, aDOMNode); + + return NS_OK; +} + +NS_IMETHODIMP +Accessible::GetRootDocument(nsIAccessibleDocument** aRootDocument) +{ + NS_ENSURE_ARG_POINTER(aRootDocument); + + NS_IF_ADDREF(*aRootDocument = RootAccessible()); + return NS_OK; +} + +NS_IMETHODIMP +Accessible::GetLanguage(nsAString& aLanguage) +{ + Language(aLanguage); + return NS_OK; +} + +NS_IMETHODIMP +Accessible::GetName(nsAString& aName) +{ + aName.Truncate(); + + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsAutoString name; + Name(name); + aName.Assign(name); + + return NS_OK; +} + +ENameValueFlag +Accessible::Name(nsString& aName) +{ + aName.Truncate(); + + if (!HasOwnContent()) + return eNameOK; + + ARIAName(aName); + if (!aName.IsEmpty()) + return eNameOK; + + nsCOMPtr xblAccessible(do_QueryInterface(mContent)); + if (xblAccessible) { + xblAccessible->GetAccessibleName(aName); + if (!aName.IsEmpty()) + return eNameOK; + } + + ENameValueFlag nameFlag = NativeName(aName); + if (!aName.IsEmpty()) + return nameFlag; + + // In the end get the name from tooltip. + if (mContent->IsHTML()) { + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) { + aName.CompressWhitespace(); + return eNameFromTooltip; + } + } else if (mContent->IsXUL()) { + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) { + aName.CompressWhitespace(); + return eNameFromTooltip; + } + } else if (mContent->IsSVG()) { + // If user agents need to choose among multiple ‘desc’ or ‘title’ elements + // for processing, the user agent shall choose the first one. + for (nsIContent* childElm = mContent->GetFirstChild(); childElm; + childElm = childElm->GetNextSibling()) { + if (childElm->IsSVG(nsGkAtoms::desc)) { + nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName); + return eNameFromTooltip; + } + } + } + + if (nameFlag != eNoNameOnPurpose) + aName.SetIsVoid(true); + + return nameFlag; +} + +NS_IMETHODIMP +Accessible::GetDescription(nsAString& aDescription) +{ + if (IsDefunct()) + return NS_ERROR_FAILURE; + + nsAutoString desc; + Description(desc); + aDescription.Assign(desc); + + return NS_OK; +} + +void +Accessible::Description(nsString& aDescription) +{ + // There are 4 conditions that make an accessible have no accDescription: + // 1. it's a text node; or + // 2. It has no DHTML describedby property + // 3. it doesn't have an accName; or + // 4. its title attribute already equals to its accName nsAutoString name; + + if (!HasOwnContent() || mContent->IsNodeOfType(nsINode::eTEXT)) + return; + + nsTextEquivUtils:: + GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby, + aDescription); + + if (aDescription.IsEmpty()) { + bool isXUL = mContent->IsXUL(); + if (isXUL) { + // Try XUL description text + XULDescriptionIterator iter(Document(), mContent); + Accessible* descr = nullptr; + while ((descr = iter.Next())) { + nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(), + &aDescription); + } + } + + if (aDescription.IsEmpty()) { + // Keep the Name() method logic. + if (mContent->IsHTML()) { + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription); + } else if (mContent->IsXUL()) { + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription); + } else if (mContent->IsSVG()) { + for (nsIContent* childElm = mContent->GetFirstChild(); childElm; + childElm = childElm->GetNextSibling()) { + if (childElm->IsSVG(nsGkAtoms::desc)) { + nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, + &aDescription); + break; + } + } + } + + if (!aDescription.IsEmpty()) { + nsAutoString name; + ENameValueFlag nameFlag = Name(name); + + // Don't use tooltip for a description if it was used for a name. + if (nameFlag == eNameFromTooltip) + aDescription.Truncate(); + } + } + } + aDescription.CompressWhitespace(); +} + +NS_IMETHODIMP +Accessible::GetAccessKey(nsAString& aAccessKey) +{ + aAccessKey.Truncate(); + + if (IsDefunct()) + return NS_ERROR_FAILURE; + + AccessKey().ToString(aAccessKey); + return NS_OK; +} + +KeyBinding +Accessible::AccessKey() const +{ + if (!HasOwnContent()) + return KeyBinding(); + + uint32_t key = nsCoreUtils::GetAccessKeyFor(mContent); + if (!key && mContent->IsElement()) { + Accessible* label = nullptr; + + // Copy access key from label node. + if (mContent->IsHTML()) { + // Unless it is labeled via an ancestor