Search code examples
c++firefoxmozillaiaccessible

Accessing Mozilla's ISimpleDOMNode by using IAccessible


I have the following code:

IAccessible *pAccessible = NULL;
IServiceProvider *pServProv = NULL; 
AccessibleObjectFromWindow((HWND)0x0025093A, OBJID_CLIENT, IID_IAccessible, (void**)&pAccessible);

HRESULT hr = pAccessible->QueryInterface(IID_IServiceProvider, (void**)&pServProv);
if (SUCCEEDED(hr) && (pServProv != NULL))
{
    const GUID unused;
    ISimpleDOMDocument *pAccDoc = NULL;

    hr = pServProv->QueryService(unused, IID_ISimpleDOMDocument, (void**)&pAccDoc);

    if (SUCCEEDED(hr) && (pAccDoc != NULL))
    {
        // Success
    }
    else
    {
        // Failure
    }
}

The hard-coded HWND above is to an instance of MozillaContentWindowClass.

I can get as far as QueryService - AccessibleObjectFromWindow and QueryInterface both succeed and return non-NULL objects; however, QueryService returns 'Invalid Parameter'. I've seen other suggestions, one of which is not using QueryService - just calling QueryInterface with IID_ISimpleDom* - but those calls return a 'No Service' error.

I've also seen suggestions to navigate to the Document object, and get a reference to the node from there - but I'm not quite sure how to accomplish that (I'm new to IAccessibility).

I appreciate any insight.


Solution

  • Magic GUID provided by Mozilla

    if (pAccChild)
    {
        CComQIPtr<IServiceProvider> provider = pAccChild;
        if( provider ){
           const GUID refguid = {0x0c539790, 0x12e4, 0x11cf, 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
    
          CComPtr<ISimpleDOMNode> node;
          provider->QueryService(refguid, IID_ISimpleDOMNode, (void**)&node);
          CComQIPtr<ISimpleDOMDocument> ffdocument = node;
          if (node) {
            ffdocument->get_URL(&DOM_string);
            if (DOM_string != 0) { 
                addressbar = (const wchar_t *)_bstr_t(DOM_string, FALSE); 
            }
          }
    

    Now, between AccessibleObjectFromWindow and getting the provider, you may need to move around the accessible heirarchy, that can be a bit messy.

    Have a look at this Code Project - XMSAALib for suggestions.

    Here is an example of some of the tree walking covering various issues. Modified from the original to use ATL smart pointers and some bug fixes (or introduced ;) )

    //-----------------------------------------------------------------------------
    bool CXMSAALib::EnumAccessible(IAccessible *pIAccParent, IAccessibleEnumProc& lpEnumAccessibleProc )
    {
        _ASSERTE(pIAccParent);
    
        // 2 ways to go through the children
        // * some parents will support the Enum(erator) interface where child ids may not be a contiguous sequence, and children may be returned as id or IAccessible directly
        // * others support the accChild function only where the count and ids should be contiguous.
    
        CComQIPtr<IEnumVARIANT> pEnum = pIAccParent;
        if( pEnum )
            pEnum->Reset();
    
        // get child count
        long nChildren = 0;
        if( FAILED( pIAccParent->get_accChildCount(&nChildren) ) )
            nChildren = 0;
        //TRACE(_T("nChildren=%d\n"), nChildren);
    
        bool bContinue = true;
        // skip 0 (self)
        for (long index = 1; (index <= nChildren) && bContinue; index++)
        {
            HRESULT hr =0;
            VARIANT varChildRef;
            VariantInit(&varChildRef);
            if (pEnum)
            {
                unsigned long nFetched = 0;
                hr = pEnum->Next(1, &varChildRef, &nFetched);
                //children may be returned as lVal id or IAccessible directly
                if (!SUCCEEDED(hr) || !nFetched )
                {
                    bContinue = false;
                    break;
                }
            }
            else
            {
                varChildRef.vt = VT_I4;
                varChildRef.lVal = index;
            }
    
            // IAccessible doesn't always allow indirect access to children that are also of type IAccessible
            // change the focus to the child element if we can
            VARIANT varChild;
            VariantInit(&varChild);
            CComPtr<IAccessible> pIAccChild;
            FocusOnChild( pIAccParent, varChildRef, pIAccChild, varChild );
    
            bContinue = lpEnumAccessibleProc(pIAccChild, varChild);
    
            if ( bContinue 
                && pIAccChild 
                && CHILDID_SELF == varChild.lVal )
            {
    
                bContinue = EnumAccessible(pIAccChild, lpEnumAccessibleProc);
    
            }
    
            VariantClear(&varChild);
        }
    
        return bContinue;
    }
    
    //-----------------------------------------------------------------------------
    bool CXMSAALib::EnumAccessible(HWND hwnd, IAccessibleEnumProc& lpEnumAccessibleProc) 
    {
        if (::IsWindow(hwnd))
        {
            CComPtr<IAccessible> pIAccParent;
    
            HRESULT hr = AccessibleObjectFromWindow(hwnd, OBJID_CLIENT, IID_IAccessible, (void**)&pIAccParent);
            if (SUCCEEDED(hr) && pIAccParent)
            {
                VARIANT varChild;
                VariantInit(&varChild);
                varChild.vt = VT_I4;
                varChild.lVal = CHILDID_SELF;
                if( lpEnumAccessibleProc(pIAccParent, varChild) ) {
                    EnumAccessible(pIAccParent, lpEnumAccessibleProc, nLevel+1);
                }
    
                VariantClear(&varChild);
                return true;
            }
        }
        return false;
    }
    
    //-----------------------------------------------------------------------------
    void CXMSAALib::FocusOnChild( IAccessible * pIAccParent, VARIANT &varChildRef, CComPtr<IAccessible> &pIAccChild, VARIANT &varChild ) 
    {
        // get IDispatch interface for the child
        CComPtr<IDispatch> pDisp;
        if (varChildRef.vt == VT_I4)
        {
            pIAccParent->get_accChild(varChildRef, &pDisp);
        }
        else if (varChildRef.vt == VT_DISPATCH)
        {
            pDisp = varChildRef.pdispVal;
        }
    
        // get IAccessible interface for the child
        CComQIPtr<IAccessible> pCAcc(pDisp);
    
        if (pCAcc && pCAcc != pIAccParent )
        {
            VariantInit(&varChild);
            varChild.vt = VT_I4;
            varChild.lVal = CHILDID_SELF;
            pIAccChild = pCAcc;
        }
        else
        {
            pIAccChild = pIAccParent;
            varChild = varChildRef;
        }
    }