/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2007 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Alexander Surkov (original author) * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsAccessibilityUtils.h" #include "nsIAccessibleStates.h" #include "nsIAccessibleTypes.h" #include "nsPIAccessible.h" #include "nsPIAccessNode.h" #include "nsAccessibleEventData.h" #include "nsAccessible.h" #include "nsARIAMap.h" #include "nsIDocument.h" #include "nsIDOMAbstractView.h" #include "nsIDOM3Node.h" #include "nsIDOMDocument.h" #include "nsIDOMDocumentView.h" #include "nsIDOMDocumentXBL.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLElement.h" #include "nsIDOMNodeList.h" #include "nsIDOMRange.h" #include "nsIDOMXULContainerElement.h" #include "nsIDOMXULSelectCntrlEl.h" #include "nsIDOMXULSelectCntrlItemEl.h" #include "nsIDOMWindowInternal.h" #include "nsIEventListenerManager.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsIScrollableFrame.h" #include "nsIEventStateManager.h" #include "nsISelection2.h" #include "nsISelectionController.h" #include "nsGUIEvent.h" #include "nsContentCID.h" #include "nsComponentManagerUtils.h" #include "nsIInterfaceRequestorUtils.h" #include "nsWhitespaceTokenizer.h" static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); void nsAccUtils::GetAccAttr(nsIPersistentProperties *aAttributes, nsIAtom *aAttrName, nsAString& aAttrValue) { aAttrValue.Truncate(); nsCAutoString attrName; aAttrName->ToUTF8String(attrName); aAttributes->GetStringProperty(attrName, aAttrValue); } void nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes, nsIAtom *aAttrName, const nsAString& aAttrValue) { nsAutoString oldValue; nsCAutoString attrName; aAttrName->ToUTF8String(attrName); aAttributes->SetStringProperty(attrName, aAttrValue, oldValue); } void nsAccUtils::GetAccGroupAttrs(nsIPersistentProperties *aAttributes, PRInt32 *aLevel, PRInt32 *aPosInSet, PRInt32 *aSetSize) { *aLevel = 0; *aPosInSet = 0; *aSetSize = 0; nsAutoString value; PRInt32 error = NS_OK; GetAccAttr(aAttributes, nsAccessibilityAtoms::level, value); if (!value.IsEmpty()) { PRInt32 level = value.ToInteger(&error); if (NS_SUCCEEDED(error)) *aLevel = level; } GetAccAttr(aAttributes, nsAccessibilityAtoms::posinset, value); if (!value.IsEmpty()) { PRInt32 posInSet = value.ToInteger(&error); if (NS_SUCCEEDED(error)) *aPosInSet = posInSet; } GetAccAttr(aAttributes, nsAccessibilityAtoms::setsize, value); if (!value.IsEmpty()) { PRInt32 sizeSet = value.ToInteger(&error); if (NS_SUCCEEDED(error)) *aSetSize = sizeSet; } } PRBool nsAccUtils::HasAccGroupAttrs(nsIPersistentProperties *aAttributes) { nsAutoString value; GetAccAttr(aAttributes, nsAccessibilityAtoms::setsize, value); if (!value.IsEmpty()) { GetAccAttr(aAttributes, nsAccessibilityAtoms::posinset, value); return !value.IsEmpty(); } return PR_FALSE; } void nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes, PRInt32 aLevel, PRInt32 aPosInSet, PRInt32 aSetSize) { nsAutoString value; if (aLevel) { value.AppendInt(aLevel); SetAccAttr(aAttributes, nsAccessibilityAtoms::level, value); } if (aSetSize && aPosInSet) { value.Truncate(); value.AppendInt(aPosInSet); SetAccAttr(aAttributes, nsAccessibilityAtoms::posinset, value); value.Truncate(); value.AppendInt(aSetSize); SetAccAttr(aAttributes, nsAccessibilityAtoms::setsize, value); } } void nsAccUtils::SetAccAttrsForXULSelectControlItem(nsIDOMNode *aNode, nsIPersistentProperties *aAttributes) { nsCOMPtr item(do_QueryInterface(aNode)); if (!item) return; nsCOMPtr control; item->GetControl(getter_AddRefs(control)); if (!control) return; PRUint32 itemsCount = 0; control->GetItemCount(&itemsCount); PRInt32 indexOf = 0; control->GetIndexOfItem(item, &indexOf); PRUint32 setSize = itemsCount, posInSet = indexOf; for (PRUint32 index = 0; index < itemsCount; index++) { nsCOMPtr currItem; control->GetItemAtIndex(index, getter_AddRefs(currItem)); nsCOMPtr currNode(do_QueryInterface(currItem)); nsCOMPtr itemAcc; nsAccessNode::GetAccService()->GetAccessibleFor(currNode, getter_AddRefs(itemAcc)); if (!itemAcc || nsAccessible::State(itemAcc) & nsIAccessibleStates::STATE_INVISIBLE) { setSize--; if (index < static_cast(indexOf)) posInSet--; } } SetAccGroupAttrs(aAttributes, 0, posInSet + 1, setSize); } void nsAccUtils::SetAccAttrsForXULContainerItem(nsIDOMNode *aNode, nsIPersistentProperties *aAttributes) { nsCOMPtr item(do_QueryInterface(aNode)); if (!item) return; nsCOMPtr container; item->GetParentContainer(getter_AddRefs(container)); if (!container) return; // Get item count. PRUint32 itemsCount = 0; container->GetItemCount(&itemsCount); // Get item index. PRInt32 indexOf = 0; container->GetIndexOfItem(item, &indexOf); PRUint32 setSize = itemsCount, posInSet = indexOf; for (PRUint32 index = 0; index < itemsCount; index++) { nsCOMPtr currItem; container->GetItemAtIndex(index, getter_AddRefs(currItem)); nsCOMPtr currNode(do_QueryInterface(currItem)); nsCOMPtr itemAcc; nsAccessNode::GetAccService()->GetAccessibleFor(currNode, getter_AddRefs(itemAcc)); if (!itemAcc || nsAccessible::State(itemAcc) & nsIAccessibleStates::STATE_INVISIBLE) { setSize--; if (index < static_cast(indexOf)) posInSet--; } } // Get level of the item. PRInt32 level = -1; while (container) { level++; nsCOMPtr parentContainer; container->GetParentContainer(getter_AddRefs(parentContainer)); parentContainer.swap(container); } SetAccGroupAttrs(aAttributes, level, posInSet + 1, setSize); } PRBool nsAccUtils::HasListener(nsIContent *aContent, const nsAString& aEventType) { NS_ENSURE_TRUE(aContent, PR_FALSE); nsCOMPtr listenerManager; aContent->GetListenerManager(PR_FALSE, getter_AddRefs(listenerManager)); return listenerManager && listenerManager->HasListenersFor(aEventType); } PRBool nsAccUtils::DispatchMouseEvent(PRUint32 aEventType, nsIPresShell *aPresShell, nsIContent *aContent) { nsIFrame *frame = aPresShell->GetPrimaryFrameFor(aContent); if (!frame) return PR_FALSE; nsIFrame* rootFrame = aPresShell->GetRootFrame(); if (!rootFrame) return PR_FALSE; nsCOMPtr rootWidget = rootFrame->GetWindow(); if (!rootWidget) return PR_FALSE; // Compute x and y coordinates. nsPoint point = frame->GetOffsetToExternal(rootFrame); nsSize size = frame->GetSize(); nsPresContext* presContext = aPresShell->GetPresContext(); PRInt32 x = presContext->AppUnitsToDevPixels(point.x + size.width / 2); PRInt32 y = presContext->AppUnitsToDevPixels(point.y + size.height / 2); // Fire mouse event. nsMouseEvent event(PR_TRUE, aEventType, rootWidget, nsMouseEvent::eReal, nsMouseEvent::eNormal); event.refPoint = nsIntPoint(x, y); event.clickCount = 1; event.button = nsMouseEvent::eLeftButton; event.time = PR_IntervalNow(); nsEventStatus status = nsEventStatus_eIgnore; aPresShell->HandleEventWithTarget(&event, frame, aContent, &status); return PR_TRUE; } PRUint32 nsAccUtils::GetAccessKeyFor(nsIContent *aContent) { if (!aContent) return 0; // Accesskeys are registered by @accesskey attribute only. At first check // whether it is presented on the given element to avoid the slow // nsIEventStateManager::GetRegisteredAccessKey() method. if (!aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::accesskey)) return 0; nsCOMPtr doc = aContent->GetOwnerDoc(); if (!doc) return 0; nsCOMPtr presShell = doc->GetPrimaryShell(); if (!presShell) return 0; nsPresContext *presContext = presShell->GetPresContext(); if (!presContext) return 0; nsIEventStateManager *esm = presContext->EventStateManager(); if (!esm) return 0; PRUint32 key = 0; esm->GetRegisteredAccessKey(aContent, &key); return key; } nsresult nsAccUtils::FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible, PRBool aIsAsynch) { NS_ENSURE_ARG(aAccessible); nsCOMPtr pAccessible(do_QueryInterface(aAccessible)); NS_ASSERTION(pAccessible, "Accessible doesn't implement nsPIAccessible"); nsCOMPtr event = new nsAccEvent(aEventType, aAccessible, aIsAsynch); NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); return pAccessible->FireAccessibleEvent(event); } PRBool nsAccUtils::IsAncestorOf(nsIDOMNode *aPossibleAncestorNode, nsIDOMNode *aPossibleDescendantNode) { NS_ENSURE_TRUE(aPossibleAncestorNode && aPossibleDescendantNode, PR_FALSE); nsCOMPtr loopNode = aPossibleDescendantNode; nsCOMPtr parentNode; while (NS_SUCCEEDED(loopNode->GetParentNode(getter_AddRefs(parentNode))) && parentNode) { if (parentNode == aPossibleAncestorNode) { return PR_TRUE; } loopNode.swap(parentNode); } return PR_FALSE; } PRBool nsAccUtils::AreSiblings(nsIDOMNode *aDOMNode1, nsIDOMNode *aDOMNode2) { NS_ENSURE_TRUE(aDOMNode1 && aDOMNode2, PR_FALSE); nsCOMPtr parentNode1, parentNode2; if (NS_SUCCEEDED(aDOMNode1->GetParentNode(getter_AddRefs(parentNode1))) && NS_SUCCEEDED(aDOMNode2->GetParentNode(getter_AddRefs(parentNode2))) && parentNode1 == parentNode2) { return PR_TRUE; } return PR_FALSE; } already_AddRefed nsAccUtils::GetAncestorWithRole(nsIAccessible *aDescendant, PRUint32 aRole) { nsCOMPtr parentAccessible = aDescendant, testRoleAccessible; while (NS_SUCCEEDED(parentAccessible->GetParent(getter_AddRefs(testRoleAccessible))) && testRoleAccessible) { PRUint32 testRole; testRoleAccessible->GetFinalRole(&testRole); if (testRole == aRole) { nsIAccessible *returnAccessible = testRoleAccessible; NS_ADDREF(returnAccessible); return returnAccessible; } nsCOMPtr docAccessible = do_QueryInterface(testRoleAccessible); if (docAccessible) { break; } parentAccessible.swap(testRoleAccessible); } return nsnull; } void nsAccUtils::GetARIATreeItemParent(nsIAccessible *aStartTreeItem, nsIContent *aStartContent, nsIAccessible **aTreeItemParentResult) { *aTreeItemParentResult = nsnull; nsAutoString levelStr; PRInt32 level = 0; if (aStartContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_level, levelStr)) { // This is a tree that uses aria-level to define levels, so find the first previous // sibling accessible where level is defined to be less than the current level PRInt32 success; level = levelStr.ToInteger(&success); if (level > 1 && NS_SUCCEEDED(success)) { nsCOMPtr currentAccessible = aStartTreeItem, prevAccessible; while (PR_TRUE) { currentAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible)); currentAccessible.swap(prevAccessible); nsCOMPtr accessNode = do_QueryInterface(currentAccessible); if (!accessNode) { break; // Reached top of tree, no higher level found } PRUint32 role; currentAccessible->GetFinalRole(&role); if (role != nsIAccessibleRole::ROLE_OUTLINEITEM) continue; nsCOMPtr treeItemNode; accessNode->GetDOMNode(getter_AddRefs(treeItemNode)); nsCOMPtr treeItemContent = do_QueryInterface(treeItemNode); if (treeItemContent && treeItemContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_level, levelStr)) { if (levelStr.ToInteger(&success) < level && NS_SUCCEEDED(success)) { NS_ADDREF(*aTreeItemParentResult = currentAccessible); return; } } } } } // Possibly a tree arranged by using role="group" to organize levels // In this case the parent of the tree item will be a group and the // previous sibling of that should be the tree item parent. // Or, if the parent is something other than a tree we will return that. nsCOMPtr parentAccessible; aStartTreeItem->GetParent(getter_AddRefs(parentAccessible)); if (!parentAccessible) return; PRUint32 role; parentAccessible->GetFinalRole(&role); if (role != nsIAccessibleRole::ROLE_GROUPING) { NS_ADDREF(*aTreeItemParentResult = parentAccessible); return; // The container for the tree items } nsCOMPtr prevAccessible; parentAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible)); if (!prevAccessible) return; prevAccessible->GetFinalRole(&role); if (role == nsIAccessibleRole::ROLE_TEXT_LEAF) { // XXX Sometimes an empty text accessible is in the hierarchy here, // although the text does not appear to be rendered, GetRenderedText() says that it is // so we need to skip past it to find the true previous sibling nsCOMPtr tempAccessible = prevAccessible; tempAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible)); if (!prevAccessible) return; prevAccessible->GetFinalRole(&role); } if (role == nsIAccessibleRole::ROLE_OUTLINEITEM) { // Previous sibling of parent group is a tree item -- this is the conceptual tree item parent NS_ADDREF(*aTreeItemParentResult = prevAccessible); } } already_AddRefed nsAccUtils::GetTextAccessibleFromSelection(nsISelection *aSelection, nsIDOMNode **aNode) { // Get accessible from selection's focus DOM point (the DOM point where // selection is ended). nsCOMPtr resultNode; aSelection->GetFocusNode(getter_AddRefs(resultNode)); if (!resultNode) return nsnull; // Get DOM node that focus DOM point points to. nsCOMPtr content(do_QueryInterface(resultNode)); if (content && content->IsNodeOfType(nsINode::eELEMENT)) { PRInt32 offset = 0; aSelection->GetFocusOffset(&offset); PRInt32 childCount = static_cast(content->GetChildCount()); NS_ASSERTION(offset >= 0 && offset <= childCount, "Wrong focus offset in selection!"); // The offset can be after last child of container node that means DOM point // is placed immediately after the last child. In this case use focusNode // as result node. if (offset != childCount) { nsCOMPtr child = content->GetChildAt(offset); resultNode = do_QueryInterface(child); } } nsIAccessibilityService *accService = nsAccessNode::GetAccService(); // Get text accessible containing the result node. while (resultNode) { // Make sure to get the correct starting node for selection events inside // XBL content trees. nsCOMPtr relevantNode; nsresult rv = accService-> GetRelevantContentNodeFor(resultNode, getter_AddRefs(relevantNode)); if (NS_FAILED(rv)) return nsnull; if (relevantNode) resultNode.swap(relevantNode); nsCOMPtr content = do_QueryInterface(resultNode); if (!content || !content->IsNodeOfType(nsINode::eTEXT)) { nsCOMPtr accessible; accService->GetAccessibleFor(resultNode, getter_AddRefs(accessible)); if (accessible) { nsIAccessibleText *textAcc = nsnull; CallQueryInterface(accessible, &textAcc); if (textAcc) { if (aNode) NS_ADDREF(*aNode = resultNode); return textAcc; } } } nsCOMPtr parentNode; resultNode->GetParentNode(getter_AddRefs(parentNode)); resultNode.swap(parentNode); } NS_NOTREACHED("No nsIAccessibleText for selection change event!"); return nsnull; } nsresult nsAccUtils::ScrollSubstringTo(nsIFrame *aFrame, nsIDOMNode *aStartNode, PRInt32 aStartIndex, nsIDOMNode *aEndNode, PRInt32 aEndIndex, PRUint32 aScrollType) { PRInt16 vPercent, hPercent; ConvertScrollTypeToPercents(aScrollType, &vPercent, &hPercent); return ScrollSubstringTo(aFrame, aStartNode, aStartIndex, aEndNode, aEndIndex, vPercent, hPercent); } nsresult nsAccUtils::ScrollSubstringTo(nsIFrame *aFrame, nsIDOMNode *aStartNode, PRInt32 aStartIndex, nsIDOMNode *aEndNode, PRInt32 aEndIndex, PRInt16 aVPercent, PRInt16 aHPercent) { if (!aFrame || !aStartNode || !aEndNode) return NS_ERROR_FAILURE; nsPresContext *presContext = aFrame->PresContext(); nsCOMPtr scrollToRange = do_CreateInstance(kRangeCID); NS_ENSURE_TRUE(scrollToRange, NS_ERROR_FAILURE); nsCOMPtr selCon; aFrame->GetSelectionController(presContext, getter_AddRefs(selCon)); NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE); scrollToRange->SetStart(aStartNode, aStartIndex); scrollToRange->SetEnd(aEndNode, aEndIndex); nsCOMPtr selection1; selCon->GetSelection(nsISelectionController::SELECTION_ACCESSIBILITY, getter_AddRefs(selection1)); nsCOMPtr selection(do_QueryInterface(selection1)); if (selection) { selection->RemoveAllRanges(); selection->AddRange(scrollToRange); selection->ScrollIntoView(nsISelectionController::SELECTION_ANCHOR_REGION, PR_TRUE, aVPercent, aHPercent); selection->CollapseToStart(); } return NS_OK; } void nsAccUtils::ScrollFrameToPoint(nsIFrame *aScrollableFrame, nsIFrame *aFrame, const nsIntPoint& aPoint) { nsIScrollableFrame *scrollableFrame = nsnull; CallQueryInterface(aScrollableFrame, &scrollableFrame); if (!scrollableFrame) return; nsPresContext *presContext = aFrame->PresContext(); nsIntRect frameRect = aFrame->GetScreenRectExternal(); PRInt32 devDeltaX = aPoint.x - frameRect.x; PRInt32 devDeltaY = aPoint.y - frameRect.y; nsPoint deltaPoint; deltaPoint.x = presContext->DevPixelsToAppUnits(devDeltaX); deltaPoint.y = presContext->DevPixelsToAppUnits(devDeltaY); nsPoint scrollPoint = scrollableFrame->GetScrollPosition(); scrollPoint -= deltaPoint; scrollableFrame->ScrollTo(scrollPoint); } void nsAccUtils::ConvertScrollTypeToPercents(PRUint32 aScrollType, PRInt16 *aVPercent, PRInt16 *aHPercent) { switch (aScrollType) { case nsIAccessibleScrollType::SCROLL_TYPE_TOP_LEFT: *aVPercent = NS_PRESSHELL_SCROLL_TOP; *aHPercent = NS_PRESSHELL_SCROLL_LEFT; break; case nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_RIGHT: *aVPercent = NS_PRESSHELL_SCROLL_BOTTOM; *aHPercent = NS_PRESSHELL_SCROLL_RIGHT; break; case nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE: *aVPercent = NS_PRESSHELL_SCROLL_TOP; *aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE; break; case nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_EDGE: *aVPercent = NS_PRESSHELL_SCROLL_BOTTOM; *aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE; break; case nsIAccessibleScrollType::SCROLL_TYPE_LEFT_EDGE: *aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE; *aHPercent = NS_PRESSHELL_SCROLL_LEFT; break; case nsIAccessibleScrollType::SCROLL_TYPE_RIGHT_EDGE: *aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE; *aHPercent = NS_PRESSHELL_SCROLL_RIGHT; break; default: *aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE; *aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE; } } nsresult nsAccUtils::ConvertToScreenCoords(PRInt32 aX, PRInt32 aY, PRUint32 aCoordinateType, nsIAccessNode *aAccessNode, nsIntPoint *aCoords) { NS_ENSURE_ARG_POINTER(aCoords); aCoords->MoveTo(aX, aY); switch (aCoordinateType) { case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE: break; case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE: { NS_ENSURE_ARG(aAccessNode); *aCoords += GetScreenCoordsForWindow(aAccessNode); break; } case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE: { NS_ENSURE_ARG(aAccessNode); *aCoords += GetScreenCoordsForParent(aAccessNode); break; } default: return NS_ERROR_INVALID_ARG; } return NS_OK; } nsresult nsAccUtils::ConvertScreenCoordsTo(PRInt32 *aX, PRInt32 *aY, PRUint32 aCoordinateType, nsIAccessNode *aAccessNode) { switch (aCoordinateType) { case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE: break; case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE: { NS_ENSURE_ARG(aAccessNode); nsIntPoint coords = GetScreenCoordsForWindow(aAccessNode); *aX -= coords.x; *aY -= coords.y; break; } case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE: { NS_ENSURE_ARG(aAccessNode); nsIntPoint coords = GetScreenCoordsForParent(aAccessNode); *aX -= coords.x; *aY -= coords.y; break; } default: return NS_ERROR_INVALID_ARG; } return NS_OK; } nsIntPoint nsAccUtils::GetScreenCoordsForWindow(nsIDOMNode *aNode) { nsIntPoint coords(0, 0); nsCOMPtr treeItem(GetDocShellTreeItemFor(aNode)); if (!treeItem) return coords; nsCOMPtr rootTreeItem; treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem)); nsCOMPtr domDoc = do_GetInterface(rootTreeItem); nsCOMPtr docView(do_QueryInterface(domDoc)); if (!docView) return coords; nsCOMPtr abstractView; docView->GetDefaultView(getter_AddRefs(abstractView)); nsCOMPtr windowInter(do_QueryInterface(abstractView)); if (!windowInter) return coords; windowInter->GetScreenX(&coords.x); windowInter->GetScreenY(&coords.y); return coords; } nsIntPoint nsAccUtils::GetScreenCoordsForWindow(nsIAccessNode *aAccessNode) { nsCOMPtr DOMNode; aAccessNode->GetDOMNode(getter_AddRefs(DOMNode)); if (DOMNode) return GetScreenCoordsForWindow(DOMNode); return nsIntPoint(0, 0); } nsIntPoint nsAccUtils::GetScreenCoordsForParent(nsIAccessNode *aAccessNode) { nsCOMPtr parent; nsCOMPtr accessible(do_QueryInterface(aAccessNode)); if (accessible) { nsCOMPtr parentAccessible; accessible->GetParent(getter_AddRefs(parentAccessible)); parent = do_QueryInterface(parentAccessible); } else { nsCOMPtr parentAccessNode; aAccessNode->GetParentNode(getter_AddRefs(parentAccessNode)); parent = do_QueryInterface(parentAccessNode); } if (!parent) return nsIntPoint(0, 0); nsIFrame *parentFrame = parent->GetFrame(); if (!parentFrame) return nsIntPoint(0, 0); nsIntRect parentRect = parentFrame->GetScreenRectExternal(); return nsIntPoint(parentRect.x, parentRect.y); } already_AddRefed nsAccUtils::GetDocShellTreeItemFor(nsIDOMNode *aNode) { if (!aNode) return nsnull; nsCOMPtr domDoc; aNode->GetOwnerDocument(getter_AddRefs(domDoc)); nsCOMPtr doc(do_QueryInterface(domDoc)); if (!doc) doc = do_QueryInterface(aNode); NS_ASSERTION(doc, "No document for node passed in"); NS_ENSURE_TRUE(doc, nsnull); nsCOMPtr container = doc->GetContainer(); nsIDocShellTreeItem *docShellTreeItem = nsnull; if (container) CallQueryInterface(container, &docShellTreeItem); return docShellTreeItem; } PRBool nsAccUtils::GetID(nsIContent *aContent, nsAString& aID) { nsIAtom *idAttribute = aContent->GetIDAttributeName(); return idAttribute ? aContent->GetAttr(kNameSpaceID_None, idAttribute, aID) : PR_FALSE; } PRBool nsAccUtils::IsXLink(nsIContent *aContent) { if (!aContent) return PR_FALSE; return aContent->AttrValueIs(kNameSpaceID_XLink, nsAccessibilityAtoms::type, nsAccessibilityAtoms::simple, eCaseMatters) && aContent->HasAttr(kNameSpaceID_XLink, nsAccessibilityAtoms::href); } nsIContent* nsAccUtils::FindNeighbourPointingToNode(nsIContent *aForNode, nsIAtom *aRelationAttr, nsIAtom *aTagName, PRUint32 aAncestorLevelsToSearch) { return FindNeighbourPointingToNode(aForNode, &aRelationAttr, 1, aTagName, aAncestorLevelsToSearch); } nsIContent* nsAccUtils::FindNeighbourPointingToNode(nsIContent *aForNode, nsIAtom **aRelationAttrs, PRUint32 aAttrNum, nsIAtom *aTagName, PRUint32 aAncestorLevelsToSearch) { nsCOMPtr binding; nsAutoString controlID; if (!nsAccUtils::GetID(aForNode, controlID)) { binding = aForNode->GetBindingParent(); if (binding == aForNode) return nsnull; aForNode->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::anonid, controlID); if (controlID.IsEmpty()) return nsnull; } // Look for label in subtrees of nearby ancestors PRUint32 count = 0; nsIContent *labelContent = nsnull; nsIContent *prevSearched = nsnull; while (!labelContent && ++count <= aAncestorLevelsToSearch && (aForNode = aForNode->GetParent()) != nsnull) { if (aForNode == binding) { // When we reach the binding parent, make sure to check // all of its anonymous child subtrees nsCOMPtr doc = aForNode->GetCurrentDoc(); nsCOMPtr xblDoc(do_QueryInterface(doc)); if (!xblDoc) return nsnull; nsCOMPtr nodes; nsCOMPtr forElm(do_QueryInterface(aForNode)); xblDoc->GetAnonymousNodes(forElm, getter_AddRefs(nodes)); if (!nodes) return nsnull; PRUint32 length; nsresult rv = nodes->GetLength(&length); if (NS_FAILED(rv)) return nsnull; for (PRUint32 index = 0; index < length && !labelContent; index++) { nsCOMPtr node; rv = nodes->Item(index, getter_AddRefs(node)); if (NS_FAILED(rv)) return nsnull; nsCOMPtr content = do_QueryInterface(node); if (!content) return nsnull; if (content != prevSearched) { labelContent = FindDescendantPointingToID(&controlID, content, aRelationAttrs, aAttrNum, nsnull, aTagName); } } break; } labelContent = FindDescendantPointingToID(&controlID, aForNode, aRelationAttrs, aAttrNum, prevSearched, aTagName); prevSearched = aForNode; } return labelContent; } // Pass in aAriaProperty = null and aRelationAttr == nsnull if any