/* -*- 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 "nsCoreUtils.h" #include "nsIAccessibleTypes.h" #include "nsAccessNode.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 "nsIDOMViewCSS.h" #include "nsIDOMWindowInternal.h" #include "nsIDOMXULElement.h" #include "nsIDocShell.h" #include "nsIDocumentViewer.h" #include "nsIContentViewer.h" #include "nsIEventListenerManager.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsIScrollableFrame.h" #include "nsIEventStateManager.h" #include "nsISelection2.h" #include "nsISelectionController.h" #include "nsPIDOMWindow.h" #include "nsGUIEvent.h" #include "nsContentCID.h" #include "nsComponentManagerUtils.h" #include "nsIInterfaceRequestorUtils.h" static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); PRBool nsCoreUtils::HasListener(nsIContent *aContent, const nsAString& aEventType) { NS_ENSURE_TRUE(aContent, PR_FALSE); nsIEventListenerManager* listenerManager = aContent->GetListenerManager(PR_FALSE); return listenerManager && listenerManager->HasListenersFor(aEventType); } void nsCoreUtils::DispatchClickEvent(nsITreeBoxObject *aTreeBoxObj, PRInt32 aRowIndex, nsITreeColumn *aColumn, const nsCString& aPseudoElt) { nsCOMPtr tcElm; aTreeBoxObj->GetTreeBody(getter_AddRefs(tcElm)); if (!tcElm) return; nsCOMPtr tcContent(do_QueryInterface(tcElm)); nsIDocument *document = tcContent->GetCurrentDoc(); if (!document) return; nsIPresShell *presShell = nsnull; presShell = document->GetPrimaryShell(); if (!presShell) return; // Ensure row is visible. aTreeBoxObj->EnsureRowIsVisible(aRowIndex); // Calculate x and y coordinates. PRInt32 x = 0, y = 0, width = 0, height = 0; nsresult rv = aTreeBoxObj->GetCoordsForCellItem(aRowIndex, aColumn, aPseudoElt, &x, &y, &width, &height); if (NS_FAILED(rv)) return; nsCOMPtr tcXULElm(do_QueryInterface(tcElm)); nsCOMPtr tcBoxObj; tcXULElm->GetBoxObject(getter_AddRefs(tcBoxObj)); PRInt32 tcX = 0; tcBoxObj->GetX(&tcX); PRInt32 tcY = 0; tcBoxObj->GetY(&tcY); // Dispatch mouse events. nsIFrame* tcFrame = presShell->GetPrimaryFrameFor(tcContent); nsIFrame* rootFrame = presShell->GetRootFrame(); nsPoint offset; nsIWidget *rootWidget = rootFrame->GetViewExternal()->GetNearestWidget(&offset); nsPresContext* presContext = presShell->GetPresContext(); PRInt32 cnvdX = presContext->CSSPixelsToDevPixels(tcX + x + 1) + presContext->AppUnitsToDevPixels(offset.x); PRInt32 cnvdY = presContext->CSSPixelsToDevPixels(tcY + y + 1) + presContext->AppUnitsToDevPixels(offset.y); DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, cnvdX, cnvdY, tcContent, tcFrame, presShell, rootWidget); DispatchMouseEvent(NS_MOUSE_BUTTON_UP, cnvdX, cnvdY, tcContent, tcFrame, presShell, rootWidget); } PRBool nsCoreUtils::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. DispatchMouseEvent(aEventType, x, y, aContent, frame, aPresShell, rootWidget); return PR_TRUE; } void nsCoreUtils::DispatchMouseEvent(PRUint32 aEventType, PRInt32 aX, PRInt32 aY, nsIContent *aContent, nsIFrame *aFrame, nsIPresShell *aPresShell, nsIWidget *aRootWidget) { nsMouseEvent event(PR_TRUE, aEventType, aRootWidget, nsMouseEvent::eReal, nsMouseEvent::eNormal); event.refPoint = nsIntPoint(aX, aY); event.clickCount = 1; event.button = nsMouseEvent::eLeftButton; event.time = PR_IntervalNow(); nsEventStatus status = nsEventStatus_eIgnore; aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status); } PRUint32 nsCoreUtils::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; } already_AddRefed nsCoreUtils::GetDOMElementFor(nsIDOMNode *aNode) { nsCOMPtr node(do_QueryInterface(aNode)); nsIDOMElement *element = nsnull; if (node->IsNodeOfType(nsINode::eELEMENT)) CallQueryInterface(node, &element); else if (node->IsNodeOfType(nsINode::eTEXT)) { nsCOMPtr nodeParent = node->GetNodeParent(); NS_ASSERTION(nodeParent, "Text node has no parent!"); if (nodeParent) CallQueryInterface(nodeParent, &element); } else if (node->IsNodeOfType(nsINode::eDOCUMENT)) { nsCOMPtr htmlDoc(do_QueryInterface(node)); if (htmlDoc) { nsCOMPtr bodyElement; htmlDoc->GetBody(getter_AddRefs(bodyElement)); if (bodyElement) { CallQueryInterface(bodyElement, &element); return element; } } nsCOMPtr domDoc(do_QueryInterface(node)); domDoc->GetDocumentElement(&element); } return element; } already_AddRefed nsCoreUtils::GetDOMNodeFromDOMPoint(nsIDOMNode *aNode, PRUint32 aOffset) { nsIDOMNode *resultNode = nsnull; nsCOMPtr content(do_QueryInterface(aNode)); if (content && content->IsNodeOfType(nsINode::eELEMENT)) { PRUint32 childCount = content->GetChildCount(); NS_ASSERTION(aOffset >= 0 && aOffset <= childCount, "Wrong offset of the DOM point!"); // 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 the DOM node // from the given DOM point is used as result node. if (aOffset != childCount) { CallQueryInterface(content->GetChildAt(aOffset), &resultNode); return resultNode; } } NS_IF_ADDREF(resultNode = aNode); return resultNode; } nsIContent* nsCoreUtils::GetRoleContent(nsIDOMNode *aDOMNode) { nsCOMPtr content(do_QueryInterface(aDOMNode)); if (!content) { nsCOMPtr domDoc(do_QueryInterface(aDOMNode)); if (domDoc) { nsCOMPtr htmlDoc(do_QueryInterface(aDOMNode)); if (htmlDoc) { nsCOMPtr bodyElement; htmlDoc->GetBody(getter_AddRefs(bodyElement)); content = do_QueryInterface(bodyElement); } else { nsCOMPtr docElement; domDoc->GetDocumentElement(getter_AddRefs(docElement)); content = do_QueryInterface(docElement); } } } return content; } PRBool nsCoreUtils::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 nsCoreUtils::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; } nsresult nsCoreUtils::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 nsCoreUtils::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 nsCoreUtils::ScrollFrameToPoint(nsIFrame *aScrollableFrame, nsIFrame *aFrame, const nsIntPoint& aPoint) { nsIScrollableFrame *scrollableFrame = do_QueryFrame(aScrollableFrame); 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 nsCoreUtils::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; } } nsIntPoint nsCoreUtils::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; } already_AddRefed nsCoreUtils::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; } nsIFrame* nsCoreUtils::GetFrameFor(nsIDOMElement *aElm) { nsCOMPtr shell = GetPresShellFor(aElm); if (!shell) return nsnull; nsCOMPtr content(do_QueryInterface(aElm)); if (!content) return nsnull; return shell->GetPrimaryFrameFor(content); } PRBool nsCoreUtils::IsCorrectFrameType(nsIFrame *aFrame, nsIAtom *aAtom) { NS_ASSERTION(aFrame != nsnull, "aFrame is null in call to IsCorrectFrameType!"); NS_ASSERTION(aAtom != nsnull, "aAtom is null in call to IsCorrectFrameType!"); return aFrame->GetType() == aAtom; } already_AddRefed nsCoreUtils::GetPresShellFor(nsIDOMNode *aNode) { nsCOMPtr domDocument; aNode->GetOwnerDocument(getter_AddRefs(domDocument)); nsCOMPtr doc(do_QueryInterface(domDocument)); if (!doc) // This is necessary when the node is the document node doc = do_QueryInterface(aNode); nsIPresShell *presShell = nsnull; if (doc) { presShell = doc->GetPrimaryShell(); NS_IF_ADDREF(presShell); } return presShell; } already_AddRefed nsCoreUtils::GetDOMNodeForContainer(nsIDocShellTreeItem *aContainer) { nsCOMPtr shell = do_QueryInterface(aContainer); nsCOMPtr cv; shell->GetContentViewer(getter_AddRefs(cv)); if (!cv) return nsnull; nsCOMPtr docv(do_QueryInterface(cv)); if (!docv) return nsnull; nsCOMPtr doc; docv->GetDocument(getter_AddRefs(doc)); if (!doc) return nsnull; nsIDOMNode* node = nsnull; CallQueryInterface(doc.get(), &node); return node; } PRBool nsCoreUtils::GetID(nsIContent *aContent, nsAString& aID) { nsIAtom *idAttribute = aContent->GetIDAttributeName(); return idAttribute ? aContent->GetAttr(kNameSpaceID_None, idAttribute, aID) : PR_FALSE; } PRBool nsCoreUtils::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* nsCoreUtils::FindNeighbourPointingToNode(nsIContent *aForNode, nsIAtom *aRelationAttr, nsIAtom *aTagName, PRUint32 aAncestorLevelsToSearch) { return FindNeighbourPointingToNode(aForNode, &aRelationAttr, 1, aTagName, aAncestorLevelsToSearch); } nsIContent* nsCoreUtils::FindNeighbourPointingToNode(nsIContent *aForNode, nsIAtom **aRelationAttrs, PRUint32 aAttrNum, nsIAtom *aTagName, PRUint32 aAncestorLevelsToSearch) { nsAutoString controlID; if (!nsCoreUtils::GetID(aForNode, controlID)) { if (!aForNode->IsInAnonymousSubtree()) return nsnull; aForNode->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::anonid, controlID); if (controlID.IsEmpty()) return nsnull; } // Look for label in subtrees of nearby ancestors nsCOMPtr binding(aForNode->GetBindingParent()); 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