michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ 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 "sdnAccessible-inl.h" michael@0: #include "ISimpleDOMNode_i.c" michael@0: michael@0: #include "DocAccessibleWrap.h" michael@0: michael@0: #include "nsAttrName.h" michael@0: #include "nsCoreUtils.h" michael@0: #include "nsIAccessibleTypes.h" michael@0: #include "nsIDOMHTMLElement.h" michael@0: #include "nsIDOMCSSStyleDeclaration.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsWinUtils.h" michael@0: michael@0: #include "nsAutoPtr.h" michael@0: michael@0: #include "mozilla/dom/Element.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::a11y; michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::QueryInterface(REFIID aREFIID, void** aInstancePtr) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aInstancePtr) michael@0: return E_FAIL; michael@0: *aInstancePtr = nullptr; michael@0: michael@0: if (aREFIID == IID_ISimpleDOMNode) { michael@0: *aInstancePtr = static_cast(this); michael@0: AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: AccessibleWrap* accessible = static_cast(GetAccessible()); michael@0: if (accessible) michael@0: return accessible->QueryInterface(aREFIID, aInstancePtr); michael@0: michael@0: // IUnknown* is the canonical one if and only if this accessible doesn't have michael@0: // an accessible. michael@0: if (aREFIID == IID_IUnknown) { michael@0: *aInstancePtr = static_cast(this); michael@0: AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: return E_NOINTERFACE; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_nodeInfo(BSTR __RPC_FAR* aNodeName, michael@0: short __RPC_FAR* aNameSpaceID, michael@0: BSTR __RPC_FAR* aNodeValue, michael@0: unsigned int __RPC_FAR* aNumChildren, michael@0: unsigned int __RPC_FAR* aUniqueID, michael@0: unsigned short __RPC_FAR* aNodeType) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNodeName || !aNameSpaceID || !aNodeValue || !aNumChildren || michael@0: !aUniqueID || !aNodeType) michael@0: return E_INVALIDARG; michael@0: michael@0: *aNodeName = nullptr; michael@0: *aNameSpaceID = 0; michael@0: *aNodeValue = nullptr; michael@0: *aNumChildren = 0; michael@0: *aUniqueID = 0; michael@0: *aNodeType = 0; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsCOMPtr DOMNode(do_QueryInterface(mNode)); michael@0: michael@0: uint16_t nodeType = 0; michael@0: DOMNode->GetNodeType(&nodeType); michael@0: *aNodeType = static_cast(nodeType); michael@0: michael@0: if (*aNodeType != NODETYPE_TEXT) { michael@0: nsAutoString nodeName; michael@0: DOMNode->GetNodeName(nodeName); michael@0: *aNodeName = ::SysAllocString(nodeName.get()); michael@0: } michael@0: michael@0: nsAutoString nodeValue; michael@0: DOMNode->GetNodeValue(nodeValue); michael@0: *aNodeValue = ::SysAllocString(nodeValue.get()); michael@0: michael@0: *aNameSpaceID = mNode->IsNodeOfType(nsINode::eCONTENT) ? michael@0: static_cast(mNode->AsContent()->GetNameSpaceID()) : 0; michael@0: michael@0: // This is a unique ID for every content node. The 3rd party accessibility michael@0: // application can compare this to the childID we return for events such as michael@0: // focus events, to correlate back to data nodes in their internal object michael@0: // model. michael@0: Accessible* accessible = GetAccessible(); michael@0: *aUniqueID = - NS_PTR_TO_INT32(accessible ? accessible->UniqueID() : michael@0: static_cast(this)); michael@0: michael@0: *aNumChildren = mNode->GetChildCount(); michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_attributes(unsigned short aMaxAttribs, michael@0: BSTR __RPC_FAR* aAttribNames, michael@0: short __RPC_FAR* aNameSpaceIDs, michael@0: BSTR __RPC_FAR* aAttribValues, michael@0: unsigned short __RPC_FAR* aNumAttribs) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aAttribNames || !aNameSpaceIDs || !aAttribValues || !aNumAttribs) michael@0: return E_INVALIDARG; michael@0: michael@0: *aNumAttribs = 0; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (!mNode->IsElement()) michael@0: return S_FALSE; michael@0: michael@0: dom::Element* elm = mNode->AsElement(); michael@0: uint32_t numAttribs = elm->GetAttrCount(); michael@0: if (numAttribs > aMaxAttribs) michael@0: numAttribs = aMaxAttribs; michael@0: michael@0: *aNumAttribs = static_cast(numAttribs); michael@0: michael@0: for (uint32_t index = 0; index < numAttribs; index++) { michael@0: aNameSpaceIDs[index] = 0; michael@0: aAttribValues[index] = aAttribNames[index] = nullptr; michael@0: nsAutoString attributeValue; michael@0: michael@0: const nsAttrName* name = elm->GetAttrNameAt(index); michael@0: aNameSpaceIDs[index] = static_cast(name->NamespaceID()); michael@0: aAttribNames[index] = ::SysAllocString(name->LocalName()->GetUTF16String()); michael@0: elm->GetAttr(name->NamespaceID(), name->LocalName(), attributeValue); michael@0: aAttribValues[index] = ::SysAllocString(attributeValue.get()); michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_attributesForNames(unsigned short aMaxAttribs, michael@0: BSTR __RPC_FAR* aAttribNames, michael@0: short __RPC_FAR* aNameSpaceID, michael@0: BSTR __RPC_FAR* aAttribValues) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aAttribNames || !aNameSpaceID || !aAttribValues) michael@0: return E_INVALIDARG; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (!mNode->IsElement()) michael@0: return S_FALSE; michael@0: michael@0: nsCOMPtr domElement(do_QueryInterface(mNode)); michael@0: nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance(); michael@0: michael@0: int32_t index = 0; michael@0: for (index = 0; index < aMaxAttribs; index++) { michael@0: aAttribValues[index] = nullptr; michael@0: if (aAttribNames[index]) { michael@0: nsAutoString attributeValue, nameSpaceURI; michael@0: nsAutoString attributeName(nsDependentString( michael@0: static_cast(aAttribNames[index]))); michael@0: michael@0: nsresult rv = NS_OK; michael@0: if (aNameSpaceID[index]>0 && michael@0: NS_SUCCEEDED(nameSpaceManager->GetNameSpaceURI(aNameSpaceID[index], michael@0: nameSpaceURI))) { michael@0: rv = domElement->GetAttributeNS(nameSpaceURI, attributeName, michael@0: attributeValue); michael@0: } else { michael@0: rv = domElement->GetAttribute(attributeName, attributeValue); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: aAttribValues[index] = ::SysAllocString(attributeValue.get()); michael@0: } michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_computedStyle(unsigned short aMaxStyleProperties, michael@0: boolean aUseAlternateView, michael@0: BSTR __RPC_FAR* aStyleProperties, michael@0: BSTR __RPC_FAR* aStyleValues, michael@0: unsigned short __RPC_FAR* aNumStyleProperties) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aStyleProperties || aStyleValues || !aNumStyleProperties) michael@0: return E_INVALIDARG; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: *aNumStyleProperties = 0; michael@0: michael@0: if (mNode->IsNodeOfType(nsINode::eDOCUMENT)) michael@0: return S_FALSE; michael@0: michael@0: nsCOMPtr cssDecl = michael@0: nsWinUtils::GetComputedStyleDeclaration(mNode->AsContent()); michael@0: NS_ENSURE_TRUE(cssDecl, E_FAIL); michael@0: michael@0: uint32_t length = 0; michael@0: cssDecl->GetLength(&length); michael@0: michael@0: uint32_t index = 0, realIndex = 0; michael@0: for (index = realIndex = 0; index < length && realIndex < aMaxStyleProperties; michael@0: index ++) { michael@0: nsAutoString property, value; michael@0: michael@0: // Ignore -moz-* properties. michael@0: if (NS_SUCCEEDED(cssDecl->Item(index, property)) && property.CharAt(0) != '-') michael@0: cssDecl->GetPropertyValue(property, value); // Get property value michael@0: michael@0: if (!value.IsEmpty()) { michael@0: aStyleProperties[realIndex] = ::SysAllocString(property.get()); michael@0: aStyleValues[realIndex] = ::SysAllocString(value.get()); michael@0: ++realIndex; michael@0: } michael@0: } michael@0: michael@0: *aNumStyleProperties = static_cast(realIndex); michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_computedStyleForProperties(unsigned short aNumStyleProperties, michael@0: boolean aUseAlternateView, michael@0: BSTR __RPC_FAR* aStyleProperties, michael@0: BSTR __RPC_FAR* aStyleValues) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aStyleProperties || !aStyleValues) michael@0: return E_INVALIDARG; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (mNode->IsNodeOfType(nsINode::eDOCUMENT)) michael@0: return S_FALSE; michael@0: michael@0: nsCOMPtr cssDecl = michael@0: nsWinUtils::GetComputedStyleDeclaration(mNode->AsContent()); michael@0: NS_ENSURE_TRUE(cssDecl, E_FAIL); michael@0: michael@0: uint32_t index = 0; michael@0: for (index = 0; index < aNumStyleProperties; index++) { michael@0: nsAutoString value; michael@0: if (aStyleProperties[index]) michael@0: cssDecl->GetPropertyValue(nsDependentString(aStyleProperties[index]), value); // Get property value michael@0: aStyleValues[index] = ::SysAllocString(value.get()); michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::scrollTo(boolean aScrollTopLeft) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: DocAccessible* document = GetDocument(); michael@0: if (!document) // that's IsDefunct check michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (!mNode->IsContent()) michael@0: return S_FALSE; michael@0: michael@0: uint32_t scrollType = michael@0: aScrollTopLeft ? nsIAccessibleScrollType::SCROLL_TYPE_TOP_LEFT : michael@0: nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_RIGHT; michael@0: michael@0: nsCoreUtils::ScrollTo(document->PresShell(), mNode->AsContent(), scrollType); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_parentNode(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNode) michael@0: return E_INVALIDARG; michael@0: *aNode = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsINode* resultNode = mNode->GetParentNode(); michael@0: if (resultNode) { michael@0: *aNode = static_cast(new sdnAccessible(resultNode)); michael@0: (*aNode)->AddRef(); michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_firstChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNode) michael@0: return E_INVALIDARG; michael@0: *aNode = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsINode* resultNode = mNode->GetFirstChild(); michael@0: if (resultNode) { michael@0: *aNode = static_cast(new sdnAccessible(resultNode)); michael@0: (*aNode)->AddRef(); michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_lastChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNode) michael@0: return E_INVALIDARG; michael@0: *aNode = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsINode* resultNode = mNode->GetLastChild(); michael@0: if (resultNode) { michael@0: *aNode = static_cast(new sdnAccessible(resultNode)); michael@0: (*aNode)->AddRef(); michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_previousSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNode) michael@0: return E_INVALIDARG; michael@0: *aNode = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsINode* resultNode = mNode->GetPreviousSibling(); michael@0: if (resultNode) { michael@0: *aNode = static_cast(new sdnAccessible(resultNode)); michael@0: (*aNode)->AddRef(); michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_nextSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNode) michael@0: return E_INVALIDARG; michael@0: *aNode = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsINode* resultNode = mNode->GetNextSibling(); michael@0: if (resultNode) { michael@0: *aNode = static_cast(new sdnAccessible(resultNode)); michael@0: (*aNode)->AddRef(); michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_childAt(unsigned aChildIndex, michael@0: ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNode) michael@0: return E_INVALIDARG; michael@0: *aNode = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsINode* resultNode = mNode->GetChildAt(aChildIndex); michael@0: if (resultNode) { michael@0: *aNode = static_cast(new sdnAccessible(resultNode)); michael@0: (*aNode)->AddRef(); michael@0: } michael@0: michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_innerHTML(BSTR __RPC_FAR* aInnerHTML) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aInnerHTML) michael@0: return E_INVALIDARG; michael@0: *aInnerHTML = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (!mNode->IsElement()) michael@0: return S_FALSE; michael@0: michael@0: nsAutoString innerHTML; michael@0: mNode->AsElement()->GetInnerHTML(innerHTML); michael@0: if (innerHTML.IsEmpty()) michael@0: return S_FALSE; michael@0: michael@0: *aInnerHTML = ::SysAllocStringLen(innerHTML.get(), innerHTML.Length()); michael@0: if (!*aInnerHTML) michael@0: return E_OUTOFMEMORY; michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_localInterface(void __RPC_FAR *__RPC_FAR* aLocalInterface) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aLocalInterface) michael@0: return E_INVALIDARG; michael@0: *aLocalInterface = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: *aLocalInterface = this; michael@0: AddRef(); michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: sdnAccessible::get_language(BSTR __RPC_FAR* aLanguage) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aLanguage) michael@0: return E_INVALIDARG; michael@0: *aLanguage = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsAutoString language; michael@0: if (mNode->IsContent()) michael@0: nsCoreUtils::GetLanguageFor(mNode->AsContent(), nullptr, language); michael@0: if (language.IsEmpty()) { // Nothing found, so use document's language michael@0: mNode->OwnerDoc()->GetHeaderData(nsGkAtoms::headerContentLanguage, michael@0: language); michael@0: } michael@0: michael@0: if (language.IsEmpty()) michael@0: return S_FALSE; michael@0: michael@0: *aLanguage = ::SysAllocStringLen(language.get(), language.Length()); michael@0: if (!*aLanguage) michael@0: return E_OUTOFMEMORY; michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: }