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