/* -*- 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 * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2003 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Original Author: Aaron Leventhal (aaronl@netscape.com) * * Alternatively, the contents of this file may be used under the terms of * either 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 "nsAccessNodeWrap.h" #include "ISimpleDOMNode_i.c" #include "nsIAccessibilityService.h" #include "nsIAccessible.h" #include "nsIDocument.h" #include "nsIDOMCSSStyleDeclaration.h" #include "nsIDOMNodeList.h" #include "nsIDOMNSHTMLElement.h" #include "nsIDOMViewCSS.h" #include "nsIFrame.h" #include "nsINameSpaceManager.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIPresShell.h" #include "nsIScriptGlobalObject.h" #include "nsIServiceManager.h" #include "nsIServiceManager.h" /// the accessible library and cached methods HINSTANCE nsAccessNodeWrap::gmAccLib = nsnull; HINSTANCE nsAccessNodeWrap::gmUserLib = nsnull; LPFNACCESSIBLEOBJECTFROMWINDOW nsAccessNodeWrap::gmAccessibleObjectFromWindow = nsnull; LPFNNOTIFYWINEVENT nsAccessNodeWrap::gmNotifyWinEvent = nsnull; LPFNGETGUITHREADINFO nsAccessNodeWrap::gmGetGUIThreadInfo = nsnull; PRBool nsAccessNodeWrap::gIsEnumVariantSupportDisabled = 0; /* For documentation of the accessibility architecture, * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html */ /* * Class nsAccessNodeWrap */ //----------------------------------------------------- // construction //----------------------------------------------------- nsAccessNodeWrap::nsAccessNodeWrap(nsIDOMNode *aNode, nsIWeakReference* aShell): nsAccessNode(aNode, aShell) { } //----------------------------------------------------- // destruction //----------------------------------------------------- nsAccessNodeWrap::~nsAccessNodeWrap() { } //----------------------------------------------------- // IUnknown interface methods - see iunknown.h for documentation //----------------------------------------------------- STDMETHODIMP_(ULONG) nsAccessNodeWrap::AddRef() { return nsAccessNode::AddRef(); } STDMETHODIMP_(ULONG) nsAccessNodeWrap::Release() { return nsAccessNode::Release(); } STDMETHODIMP nsAccessNodeWrap::QueryInterface(REFIID iid, void** ppv) { *ppv = nsnull; if (IID_IUnknown == iid || IID_ISimpleDOMNode == iid) *ppv = NS_STATIC_CAST(ISimpleDOMNode*, this); if (nsnull == *ppv) return E_NOINTERFACE; //iid not supported. (NS_REINTERPRET_CAST(IUnknown*, *ppv))->AddRef(); return S_OK; } //----------------------------------------------------- // ISimpleDOMNode methods //----------------------------------------------------- STDMETHODIMP nsAccessNodeWrap::get_nodeInfo( /* [out] */ BSTR __RPC_FAR *aNodeName, /* [out] */ short __RPC_FAR *aNameSpaceID, /* [out] */ BSTR __RPC_FAR *aNodeValue, /* [out] */ unsigned int __RPC_FAR *aNumChildren, /* [out] */ unsigned int __RPC_FAR *aUniqueID, /* [out] */ unsigned short __RPC_FAR *aNodeType) { if (!mDOMNode) return E_FAIL; *aNodeName = nsnull; nsCOMPtr content(do_QueryInterface(mDOMNode)); PRUint16 nodeType = 0; mDOMNode->GetNodeType(&nodeType); *aNodeType=NS_STATIC_CAST(unsigned short, nodeType); if (*aNodeType != NODETYPE_TEXT) { nsAutoString nodeName; mDOMNode->GetNodeName(nodeName); *aNodeName = ::SysAllocString(nodeName.get()); } nsAutoString nodeValue; mDOMNode->GetNodeValue(nodeValue); *aNodeValue = ::SysAllocString(nodeValue.get()); PRInt32 nameSpaceID = 0; *aUniqueID = 0; // magic value of 0 means we're on the document node. if (content) { content->GetNameSpaceID(&nameSpaceID); // This is a unique ID for every content node. The 3rd party // accessibility application can compare this to the childID we // return for events such as focus events, to correlate back to // data nodes in their internal object model. *aUniqueID = content->ContentID(); } *aNameSpaceID = NS_STATIC_CAST(short, nameSpaceID); *aNumChildren = 0; PRUint32 numChildren = 0; nsCOMPtr nodeList; mDOMNode->GetChildNodes(getter_AddRefs(nodeList)); if (nodeList && NS_OK == nodeList->GetLength(&numChildren)) *aNumChildren = NS_STATIC_CAST(unsigned int, numChildren); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_attributes( /* [in] */ unsigned short aMaxAttribs, /* [length_is][size_is][out] */ BSTR __RPC_FAR *aAttribNames, /* [length_is][size_is][out] */ short __RPC_FAR *aNameSpaceIDs, /* [length_is][size_is][out] */ BSTR __RPC_FAR *aAttribValues, /* [out] */ unsigned short __RPC_FAR *aNumAttribs) { *aNumAttribs = 0; nsCOMPtr content(do_QueryInterface(mDOMNode)); if (!content) return E_FAIL; PRUint32 numAttribs = content->GetAttrCount(); if (numAttribs > aMaxAttribs) numAttribs = aMaxAttribs; *aNumAttribs = NS_STATIC_CAST(unsigned short, numAttribs); PRInt32 nameSpaceID; nsCOMPtr nameAtom, prefixAtom; for (PRUint32 index = 0; index < numAttribs; index++) { aNameSpaceIDs[index] = 0; aAttribValues[index] = aAttribNames[index] = nsnull; nsAutoString attributeValue; const char *pszAttributeName; if (NS_SUCCEEDED(content->GetAttrNameAt(index, &nameSpaceID, getter_AddRefs(nameAtom), getter_AddRefs(prefixAtom)))) { aNameSpaceIDs[index] = NS_STATIC_CAST(short, nameSpaceID); nameAtom->GetUTF8String(&pszAttributeName); aAttribNames[index] = ::SysAllocString(NS_ConvertUTF8toUCS2(pszAttributeName).get()); if (NS_SUCCEEDED(content->GetAttr(nameSpaceID, nameAtom, attributeValue))) aAttribValues[index] = ::SysAllocString(attributeValue.get()); } } return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_attributesForNames( /* [in] */ unsigned short aNumAttribs, /* [length_is][size_is][in] */ BSTR __RPC_FAR *aAttribNames, /* [length_is][size_is][in] */ short __RPC_FAR *aNameSpaceID, /* [length_is][size_is][retval] */ BSTR __RPC_FAR *aAttribValues) { nsCOMPtr domElement(do_QueryInterface(mDOMNode)); nsCOMPtr content(do_QueryInterface(mDOMNode)); if (!domElement || !content) return E_FAIL; if (!content->GetDocument()) return E_FAIL; nsCOMPtr nameSpaceManager = do_GetService(NS_NAMESPACEMANAGER_CONTRACTID); PRInt32 index; for (index = 0; index < aNumAttribs; index++) { aAttribValues[index] = nsnull; if (aAttribNames[index]) { nsAutoString attributeValue, nameSpaceURI; nsAutoString attributeName(nsDependentString(NS_STATIC_CAST(PRUnichar*,aAttribNames[index]))); nsresult rv; if (aNameSpaceID[index]>0 && NS_SUCCEEDED(nameSpaceManager->GetNameSpaceURI(aNameSpaceID[index], nameSpaceURI))) rv = domElement->GetAttributeNS(nameSpaceURI, attributeName, attributeValue); else rv = domElement->GetAttribute(attributeName, attributeValue); if (NS_SUCCEEDED(rv)) aAttribValues[index] = ::SysAllocString(attributeValue.get()); } } return S_OK; } NS_IMETHODIMP nsAccessNodeWrap::GetComputedStyleDeclaration(nsIDOMCSSStyleDeclaration **aCssDecl, PRUint32 *aLength) { nsCOMPtr domElement(do_QueryInterface(mDOMNode)); nsCOMPtr content(do_QueryInterface(mDOMNode)); if (!domElement || !content) return NS_ERROR_FAILURE; nsCOMPtr doc; if (content) doc = content->GetDocument(); if (!doc) { return NS_ERROR_FAILURE; } nsCOMPtr viewCSS(do_QueryInterface(doc->GetScriptGlobalObject())); if (!viewCSS) return NS_ERROR_FAILURE; nsCOMPtr cssDecl; nsAutoString empty; viewCSS->GetComputedStyle(domElement, empty, getter_AddRefs(cssDecl)); if (cssDecl) { *aCssDecl = cssDecl; NS_ADDREF(*aCssDecl); cssDecl->GetLength(aLength); return NS_OK; } return NS_ERROR_FAILURE; } /* To do: use media type if not null */ STDMETHODIMP nsAccessNodeWrap::get_computedStyle( /* [in] */ unsigned short aMaxStyleProperties, /* [in] */ boolean aUseAlternateView, /* [length_is][size_is][out] */ BSTR __RPC_FAR *aStyleProperties, /* [length_is][size_is][out] */ BSTR __RPC_FAR *aStyleValues, /* [out] */ unsigned short __RPC_FAR *aNumStyleProperties) { if (!mDOMNode) return E_FAIL; *aNumStyleProperties = 0; PRUint32 length; nsCOMPtr cssDecl; if (NS_FAILED(GetComputedStyleDeclaration(getter_AddRefs(cssDecl), &length))) return E_FAIL; PRUint32 index, realIndex; for (index = realIndex = 0; index < length && realIndex < aMaxStyleProperties; index ++) { nsAutoString property, value; if (NS_SUCCEEDED(cssDecl->Item(index, property)) && property.CharAt(0) != '-') // Ignore -moz-* properties cssDecl->GetPropertyValue(property, value); // Get property value if (!value.IsEmpty()) { aStyleProperties[realIndex] = ::SysAllocString(property.get()); aStyleValues[realIndex] = ::SysAllocString(value.get()); ++realIndex; } } *aNumStyleProperties = NS_STATIC_CAST(unsigned short, realIndex); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_computedStyleForProperties( /* [in] */ unsigned short aNumStyleProperties, /* [in] */ boolean aUseAlternateView, /* [length_is][size_is][in] */ BSTR __RPC_FAR *aStyleProperties, /* [length_is][size_is][out] */ BSTR __RPC_FAR *aStyleValues) { if (!mDOMNode) return E_FAIL; PRUint32 length = 0; nsCOMPtr cssDecl; nsresult rv = GetComputedStyleDeclaration(getter_AddRefs(cssDecl), &length); if (NS_FAILED(rv)) return E_FAIL; PRUint32 index; for (index = 0; index < aNumStyleProperties; index ++) { nsAutoString value; if (aStyleProperties[index]) cssDecl->GetPropertyValue(nsDependentString(NS_STATIC_CAST(PRUnichar*,aStyleProperties[index])), value); // Get property value aStyleValues[index] = ::SysAllocString(value.get()); } return S_OK; } STDMETHODIMP nsAccessNodeWrap::scrollTo(/* [in] */ boolean aScrollTopLeft) { nsCOMPtr shell(GetPresShell()); if (!mDOMNode || !shell) { return E_FAIL; } nsIFrame *frame = GetFrame(); if (frame) { PRInt32 percent = NS_PRESSHELL_SCROLL_ANYWHERE; if (aScrollTopLeft) percent = 0; return shell->ScrollFrameIntoView(frame, percent, percent); } return E_FAIL; } ISimpleDOMNode* nsAccessNodeWrap::MakeAccessNode(nsIDOMNode *node) { if (!node) return NULL; nsAccessNodeWrap *newNode = NULL; nsCOMPtr content(do_QueryInterface(node)); nsCOMPtr doc; if (content) doc = content->GetDocument(); else { // Get the document via QueryInterface, since there is no content node doc = do_QueryInterface(node); content = do_QueryInterface(node); } if (!doc) return NULL; nsCOMPtr accService(do_GetService("@mozilla.org/accessibilityService;1")); if (!accService) return NULL; ISimpleDOMNode *iNode = NULL; nsCOMPtr nsAcc; accService->GetAccessibleInWeakShell(node, mWeakShell, getter_AddRefs(nsAcc)); if (nsAcc) { nsCOMPtr accessNode(do_QueryInterface(nsAcc)); NS_ASSERTION(accessNode, "nsIAccessible impl does not inherit from nsIAccessNode"); IAccessible *msaaAccessible; nsAcc->GetNativeInterface((void**)&msaaAccessible); // addrefs msaaAccessible->QueryInterface(IID_ISimpleDOMNode, (void**)&iNode); // addrefs msaaAccessible->Release(); // Release IAccessible } else { newNode = new nsAccessNodeWrap(node, mWeakShell); if (!newNode) return NULL; newNode->Init(); iNode = NS_STATIC_CAST(ISimpleDOMNode*, newNode); iNode->AddRef(); } return iNode; } STDMETHODIMP nsAccessNodeWrap::get_parentNode(ISimpleDOMNode __RPC_FAR *__RPC_FAR *aNode) { if (!mDOMNode) return E_FAIL; nsCOMPtr node; mDOMNode->GetParentNode(getter_AddRefs(node)); *aNode = MakeAccessNode(node); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_firstChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR *aNode) { if (!mDOMNode) return E_FAIL; nsCOMPtr node; mDOMNode->GetFirstChild(getter_AddRefs(node)); *aNode = MakeAccessNode(node); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_lastChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR *aNode) { if (!mDOMNode) return E_FAIL; nsCOMPtr node; mDOMNode->GetLastChild(getter_AddRefs(node)); *aNode = MakeAccessNode(node); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_previousSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR *aNode) { if (!mDOMNode) return E_FAIL; nsCOMPtr node; mDOMNode->GetPreviousSibling(getter_AddRefs(node)); *aNode = MakeAccessNode(node); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_nextSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR *aNode) { if (!mDOMNode) return E_FAIL; nsCOMPtr node; mDOMNode->GetNextSibling(getter_AddRefs(node)); *aNode = MakeAccessNode(node); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_childAt(unsigned aChildIndex, ISimpleDOMNode __RPC_FAR *__RPC_FAR *aNode) { *aNode = nsnull; nsCOMPtr content(do_QueryInterface(mDOMNode)); if (!content) return E_FAIL; // Node already shut down nsCOMPtr node = do_QueryInterface(content->GetChildAt(aChildIndex)); if (!node) return E_FAIL; // No such child *aNode = MakeAccessNode(node); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_innerHTML(BSTR __RPC_FAR *aInnerHTML) { *aInnerHTML = nsnull; nsCOMPtr domNSElement(do_QueryInterface(mDOMNode)); if (!domNSElement) return E_FAIL; // Node already shut down nsAutoString innerHTML; domNSElement->GetInnerHTML(innerHTML); *aInnerHTML = ::SysAllocString(innerHTML.get()); return S_OK; } STDMETHODIMP nsAccessNodeWrap::get_localInterface( /* [out] */ void __RPC_FAR *__RPC_FAR *localInterface) { *localInterface = NS_STATIC_CAST(nsIAccessNode*, this); NS_ADDREF_THIS(); return S_OK; } void nsAccessNodeWrap::InitAccessibility() { if (gIsAccessibilityActive) { return; } nsCOMPtr prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (prefBranch) { prefBranch->GetBoolPref("accessibility.disableenumvariant", &gIsEnumVariantSupportDisabled); } if (!gmUserLib) { gmUserLib =::LoadLibrary("USER32.DLL"); } if (gmUserLib) { if (!gmNotifyWinEvent) gmNotifyWinEvent = (LPFNNOTIFYWINEVENT)GetProcAddress(gmUserLib,"NotifyWinEvent"); if (!gmGetGUIThreadInfo) gmGetGUIThreadInfo = (LPFNGETGUITHREADINFO)GetProcAddress(gmUserLib,"GetGUIThreadInfo"); } nsAccessNode::InitXPAccessibility(); } void nsAccessNodeWrap::ShutdownAccessibility() { if (!gIsAccessibilityActive) { return; } nsAccessNode::ShutdownXPAccessibility(); }