mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			696 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			696 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
 | |
| // 2013 The Chromium Authors. All rights reserved. Use of this source code is
 | |
| // governed by a BSD-style license that can be found in the LICENSE file.
 | |
| 
 | |
| // This class implements our accessible proxy object that handles moving
 | |
| // data back and forth between MSAA clients and CefClient renderers.
 | |
| // Sample implementation based on ui\accessibility\ax_platform_node_win.h
 | |
| 
 | |
| #include "tests/cefclient/browser/osr_accessibility_node.h"
 | |
| 
 | |
| #if defined(CEF_USE_ATL)
 | |
| 
 | |
| #include <atlbase.h>
 | |
| #include <oleacc.h>
 | |
| #include <string>
 | |
| 
 | |
| #include "tests/cefclient/browser/osr_accessibility_helper.h"
 | |
| 
 | |
| namespace client {
 | |
| 
 | |
| // Return CO_E_OBJNOTCONNECTED for accessible objects thar still exists but the
 | |
| // window and/or object it references has been destroyed.
 | |
| #define DATACHECK(node) (node) ? S_OK : CO_E_OBJNOTCONNECTED
 | |
| #define VALID_CHILDID(varChild) ((varChild.vt == VT_I4))
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| // Helper function to convert a rectangle from client coordinates to screen
 | |
| // coordinates.
 | |
| void ClientToScreen(HWND hwnd, LPRECT lpRect) {
 | |
|   if (lpRect) {
 | |
|     POINT ptTL = {lpRect->left, lpRect->top};
 | |
|     POINT ptBR = {lpRect->right, lpRect->bottom};
 | |
|     // Win32 API only provides the call for a point.
 | |
|     ClientToScreen(hwnd, &ptTL);
 | |
|     ClientToScreen(hwnd, &ptBR);
 | |
|     SetRect(lpRect, ptTL.x, ptTL.y, ptBR.x, ptBR.y);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Helper function to convert to MSAARole
 | |
| int AxRoleToMSAARole(const std::string& role_string) {
 | |
|   if (role_string == "alert")
 | |
|     return ROLE_SYSTEM_ALERT;
 | |
|   if (role_string == "application")
 | |
|     return ROLE_SYSTEM_APPLICATION;
 | |
|   if (role_string == "buttonDropDown")
 | |
|     return ROLE_SYSTEM_BUTTONDROPDOWN;
 | |
|   if (role_string == "popUpButton")
 | |
|     return ROLE_SYSTEM_BUTTONMENU;
 | |
|   if (role_string == "checkBox")
 | |
|     return ROLE_SYSTEM_CHECKBUTTON;
 | |
|   if (role_string == "comboBox")
 | |
|     return ROLE_SYSTEM_COMBOBOX;
 | |
|   if (role_string == "dialog")
 | |
|     return ROLE_SYSTEM_DIALOG;
 | |
|   if (role_string == "genericContainer")
 | |
|     return ROLE_SYSTEM_GROUPING;
 | |
|   if (role_string == "group")
 | |
|     return ROLE_SYSTEM_GROUPING;
 | |
|   if (role_string == "image")
 | |
|     return ROLE_SYSTEM_GRAPHIC;
 | |
|   if (role_string == "link")
 | |
|     return ROLE_SYSTEM_LINK;
 | |
|   if (role_string == "locationBar")
 | |
|     return ROLE_SYSTEM_GROUPING;
 | |
|   if (role_string == "menuBar")
 | |
|     return ROLE_SYSTEM_MENUBAR;
 | |
|   if (role_string == "menuItem")
 | |
|     return ROLE_SYSTEM_MENUITEM;
 | |
|   if (role_string == "menuListPopup")
 | |
|     return ROLE_SYSTEM_MENUPOPUP;
 | |
|   if (role_string == "tree")
 | |
|     return ROLE_SYSTEM_OUTLINE;
 | |
|   if (role_string == "treeItem")
 | |
|     return ROLE_SYSTEM_OUTLINEITEM;
 | |
|   if (role_string == "tab")
 | |
|     return ROLE_SYSTEM_PAGETAB;
 | |
|   if (role_string == "tabList")
 | |
|     return ROLE_SYSTEM_PAGETABLIST;
 | |
|   if (role_string == "pane")
 | |
|     return ROLE_SYSTEM_PANE;
 | |
|   if (role_string == "progressIndicator")
 | |
|     return ROLE_SYSTEM_PROGRESSBAR;
 | |
|   if (role_string == "button")
 | |
|     return ROLE_SYSTEM_PUSHBUTTON;
 | |
|   if (role_string == "radioButton")
 | |
|     return ROLE_SYSTEM_RADIOBUTTON;
 | |
|   if (role_string == "scrollBar")
 | |
|     return ROLE_SYSTEM_SCROLLBAR;
 | |
|   if (role_string == "splitter")
 | |
|     return ROLE_SYSTEM_SEPARATOR;
 | |
|   if (role_string == "slider")
 | |
|     return ROLE_SYSTEM_SLIDER;
 | |
|   if (role_string == "staticText")
 | |
|     return ROLE_SYSTEM_STATICTEXT;
 | |
|   if (role_string == "textField")
 | |
|     return ROLE_SYSTEM_TEXT;
 | |
|   if (role_string == "titleBar")
 | |
|     return ROLE_SYSTEM_TITLEBAR;
 | |
|   if (role_string == "toolbar")
 | |
|     return ROLE_SYSTEM_TOOLBAR;
 | |
|   if (role_string == "webView")
 | |
|     return ROLE_SYSTEM_GROUPING;
 | |
|   if (role_string == "window")
 | |
|     return ROLE_SYSTEM_WINDOW;
 | |
|   if (role_string == "client")
 | |
|     return ROLE_SYSTEM_CLIENT;
 | |
|   // This is the default role for MSAA.
 | |
|   return ROLE_SYSTEM_CLIENT;
 | |
| }
 | |
| 
 | |
| static inline int MiddleX(const CefRect& rect) {
 | |
|   return rect.x + rect.width / 2;
 | |
| }
 | |
| 
 | |
| static inline int MiddleY(const CefRect& rect) {
 | |
|   return rect.y + rect.height / 2;
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| struct CefIAccessible : public IAccessible {
 | |
|  public:
 | |
|   // Implement IUnknown
 | |
|   STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override;
 | |
|   STDMETHODIMP_(ULONG) AddRef() override;
 | |
|   STDMETHODIMP_(ULONG) Release() override;
 | |
| 
 | |
|   //
 | |
|   // IAccessible methods.
 | |
|   //
 | |
|   // Retrieves the child element or child object at a given point on the screen.
 | |
|   STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child) override;
 | |
| 
 | |
|   // Performs the object's default action.
 | |
|   STDMETHODIMP accDoDefaultAction(VARIANT var_id) override;
 | |
| 
 | |
|   // Retrieves the specified object's current screen location.
 | |
|   STDMETHODIMP accLocation(LONG* x_left,
 | |
|                            LONG* y_top,
 | |
|                            LONG* width,
 | |
|                            LONG* height,
 | |
|                            VARIANT var_id) override;
 | |
| 
 | |
|   // Traverses to another UI element and retrieves the object.
 | |
|   STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start, VARIANT* end) override;
 | |
| 
 | |
|   // Retrieves an IDispatch interface pointer for the specified child.
 | |
|   STDMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child) override;
 | |
| 
 | |
|   // Retrieves the number of accessible children.
 | |
|   STDMETHODIMP get_accChildCount(LONG* child_count) override;
 | |
| 
 | |
|   // Retrieves a string that describes the object's default action.
 | |
|   STDMETHODIMP get_accDefaultAction(VARIANT var_id,
 | |
|                                     BSTR* default_action) override;
 | |
| 
 | |
|   // Retrieves the tooltip description.
 | |
|   STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override;
 | |
| 
 | |
|   // Retrieves the object that has the keyboard focus.
 | |
|   STDMETHODIMP get_accFocus(VARIANT* focus_child) override;
 | |
| 
 | |
|   // Retrieves the specified object's shortcut.
 | |
|   STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id,
 | |
|                                        BSTR* access_key) override;
 | |
| 
 | |
|   // Retrieves the name of the specified object.
 | |
|   STDMETHODIMP get_accName(VARIANT var_id, BSTR* name) override;
 | |
| 
 | |
|   // Retrieves the IDispatch interface of the object's parent.
 | |
|   STDMETHODIMP get_accParent(IDispatch** disp_parent) override;
 | |
| 
 | |
|   // Retrieves information describing the role of the specified object.
 | |
|   STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role) override;
 | |
| 
 | |
|   // Retrieves the current state of the specified object.
 | |
|   STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state) override;
 | |
| 
 | |
|   // Gets the help string for the specified object.
 | |
|   STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help) override;
 | |
| 
 | |
|   // Retrieve or set the string value associated with the specified object.
 | |
|   // Setting the value is not typically used by screen readers, but it's
 | |
|   // used frequently by automation software.
 | |
|   STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override;
 | |
|   STDMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override;
 | |
| 
 | |
|   // IAccessible methods not implemented.
 | |
|   STDMETHODIMP get_accSelection(VARIANT* selected) override;
 | |
|   STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id) override;
 | |
|   STDMETHODIMP get_accHelpTopic(BSTR* help_file,
 | |
|                                 VARIANT var_id,
 | |
|                                 LONG* topic_id) override;
 | |
|   STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override;
 | |
| 
 | |
|   // Implement IDispatch
 | |
|   STDMETHODIMP GetTypeInfoCount(unsigned int FAR* pctinfo) override;
 | |
|   STDMETHODIMP GetTypeInfo(unsigned int iTInfo,
 | |
|                            LCID lcid,
 | |
|                            ITypeInfo FAR* FAR* ppTInfo) override;
 | |
|   STDMETHODIMP GetIDsOfNames(REFIID riid,
 | |
|                              OLECHAR FAR* FAR* rgszNames,
 | |
|                              unsigned int cNames,
 | |
|                              LCID lcid,
 | |
|                              DISPID FAR* rgDispId) override;
 | |
|   STDMETHODIMP Invoke(DISPID dispIdMember,
 | |
|                       REFIID riid,
 | |
|                       LCID lcid,
 | |
|                       WORD wFlags,
 | |
|                       DISPPARAMS FAR* pDispParams,
 | |
|                       VARIANT FAR* pVarResult,
 | |
|                       EXCEPINFO FAR* pExcepInfo,
 | |
|                       unsigned int FAR* puArgErr) override;
 | |
| 
 | |
|   CefIAccessible(OsrAXNode* node) : ref_count_(0), node_(node) {}
 | |
| 
 | |
|   // Remove the node reference when OsrAXNode is destroyed, so that
 | |
|   // MSAA clients get  CO_E_OBJNOTCONNECTED
 | |
|   void MarkDestroyed() { node_ = nullptr; }
 | |
| 
 | |
|  protected:
 | |
|   virtual ~CefIAccessible() {}
 | |
| 
 | |
|   // Ref Count
 | |
|   ULONG ref_count_;
 | |
|   // OsrAXNode* proxy object
 | |
|   OsrAXNode* node_;
 | |
| };
 | |
| 
 | |
| // Implement IUnknown
 | |
| // *********************
 | |
| 
 | |
| // Handles ref counting and querying for other supported interfaces.
 | |
| // We only support, IUnknown, IDispatch and IAccessible.
 | |
| STDMETHODIMP CefIAccessible::QueryInterface(REFIID riid, void** ppvObject) {
 | |
|   if (riid == IID_IAccessible)
 | |
|     *ppvObject = static_cast<IAccessible*>(this);
 | |
|   else if (riid == IID_IDispatch)
 | |
|     *ppvObject = static_cast<IDispatch*>(this);
 | |
|   else if (riid == IID_IUnknown)
 | |
|     *ppvObject = static_cast<IUnknown*>(this);
 | |
|   else
 | |
|     *ppvObject = nullptr;
 | |
| 
 | |
|   if (*ppvObject)
 | |
|     reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
 | |
| 
 | |
|   return (*ppvObject) ? S_OK : E_NOINTERFACE;
 | |
| }
 | |
| 
 | |
| // Increments COM objects refcount required by IUnknown for reference counting
 | |
| STDMETHODIMP_(ULONG) CefIAccessible::AddRef() {
 | |
|   return InterlockedIncrement((LONG volatile*)&ref_count_);
 | |
| }
 | |
| 
 | |
| STDMETHODIMP_(ULONG) CefIAccessible::Release() {
 | |
|   ULONG ulRefCnt = InterlockedDecrement((LONG volatile*)&ref_count_);
 | |
|   if (ulRefCnt == 0) {
 | |
|     // Remove reference from OsrAXNode
 | |
|     if (node_)
 | |
|       node_->Destroy();
 | |
|     delete this;
 | |
|   }
 | |
| 
 | |
|   return ulRefCnt;
 | |
| }
 | |
| 
 | |
| // Implement IAccessible
 | |
| // *********************
 | |
| 
 | |
| // Returns the parent IAccessible in the form of an IDispatch interface.
 | |
| STDMETHODIMP CefIAccessible::get_accParent(IDispatch** ppdispParent) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (ppdispParent) {
 | |
|       CefNativeAccessible* parent = node_->GetParentAccessibleObject();
 | |
|       if (!parent) {
 | |
|         // Find our parent window
 | |
|         HWND hWnd = ::GetParent(node_->GetWindowHandle());
 | |
|         // if we have a window attempt to get its IAccessible pointer
 | |
|         if (hWnd) {
 | |
|           AccessibleObjectFromWindow(hWnd, (DWORD)OBJID_CLIENT, IID_IAccessible,
 | |
|                                      (void**)(&parent));
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (parent)
 | |
|         parent->AddRef();
 | |
|       *ppdispParent = parent;
 | |
|       retCode = (*ppdispParent) ? S_OK : S_FALSE;
 | |
|     }
 | |
|   } else {
 | |
|     retCode = E_INVALIDARG;
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Returns the number of children we have for this element.
 | |
| STDMETHODIMP CefIAccessible::get_accChildCount(long* pcountChildren) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode) && pcountChildren) {
 | |
|     // Get Child node count for this from Accessibility tree
 | |
|     *pcountChildren = node_->GetChildCount();
 | |
|   } else {
 | |
|     retCode = E_INVALIDARG;
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Returns a child IAccessible object.
 | |
| STDMETHODIMP CefIAccessible::get_accChild(VARIANT varChild,
 | |
|                                           IDispatch** ppdispChild) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     int numChilds = node_->GetChildCount();
 | |
|     // Mark Leaf node if there are no child
 | |
|     if (numChilds <= 0) {
 | |
|       *ppdispChild = nullptr;
 | |
|       return S_FALSE;
 | |
|     } else {
 | |
|       if (ppdispChild && VALID_CHILDID(varChild)) {
 | |
|         if (varChild.lVal == CHILDID_SELF) {
 | |
|           *ppdispChild = this;
 | |
|         } else {
 | |
|           // Convert to 0 based index and get Child Node.
 | |
|           OsrAXNode* child = node_->ChildAtIndex(varChild.lVal - 1);
 | |
|           // Fallback to focused node
 | |
|           if (!child)
 | |
|             child = node_->GetAccessibilityHelper()->GetFocusedNode();
 | |
| 
 | |
|           *ppdispChild = child->GetNativeAccessibleObject(node_);
 | |
|         }
 | |
|         if (*ppdispChild == nullptr)
 | |
|           retCode = S_FALSE;
 | |
|         else
 | |
|           (*ppdispChild)->AddRef();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Check and returns the accessible name for element from accessibility tree
 | |
| STDMETHODIMP CefIAccessible::get_accName(VARIANT varChild, BSTR* pszName) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pszName && VALID_CHILDID(varChild)) {
 | |
|       std::wstring name = node_->AxName();
 | |
|       CComBSTR bstrResult(name.c_str());
 | |
|       *pszName = bstrResult.Detach();
 | |
|     }
 | |
|   } else {
 | |
|     retCode = E_INVALIDARG;
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Check and returns the value for element from accessibility tree
 | |
| STDMETHODIMP CefIAccessible::get_accValue(VARIANT varChild, BSTR* pszValue) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pszValue && VALID_CHILDID(varChild)) {
 | |
|       std::wstring name = node_->AxValue();
 | |
|       CComBSTR bstrResult(name.c_str());
 | |
|       *pszValue = bstrResult.Detach();
 | |
|     }
 | |
|   } else {
 | |
|     retCode = E_INVALIDARG;
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Check and returns the description for element from accessibility tree
 | |
| STDMETHODIMP CefIAccessible::get_accDescription(VARIANT varChild,
 | |
|                                                 BSTR* pszDescription) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pszDescription && VALID_CHILDID(varChild)) {
 | |
|       std::wstring name = node_->AxDescription();
 | |
|       CComBSTR bstrResult(name.c_str());
 | |
|       *pszDescription = bstrResult.Detach();
 | |
|     }
 | |
|   } else {
 | |
|     retCode = E_INVALIDARG;
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Check and returns the MSAA Role for element from accessibility tree
 | |
| STDMETHODIMP CefIAccessible::get_accRole(VARIANT varChild, VARIANT* pvarRole) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     // Get the accessibilty role and Map to MSAA Role
 | |
|     if (pvarRole) {
 | |
|       pvarRole->vt = VT_I4;
 | |
|       pvarRole->lVal = AxRoleToMSAARole(node_->AxRole());
 | |
|     } else {
 | |
|       retCode = E_INVALIDARG;
 | |
|     }
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Check and returns Accessibility State for element from accessibility tree
 | |
| STDMETHODIMP CefIAccessible::get_accState(VARIANT varChild,
 | |
|                                           VARIANT* pvarState) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pvarState) {
 | |
|       pvarState->vt = VT_I4;
 | |
|       pvarState->lVal =
 | |
|           (GetFocus() == node_->GetWindowHandle()) ? STATE_SYSTEM_FOCUSED : 0;
 | |
|       pvarState->lVal |= STATE_SYSTEM_PRESSED;
 | |
|       pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
 | |
| 
 | |
|       // For child
 | |
|       if (varChild.lVal == CHILDID_SELF) {
 | |
|         DWORD dwStyle = GetWindowLong(node_->GetWindowHandle(), GWL_STYLE);
 | |
|         pvarState->lVal |=
 | |
|             ((dwStyle & WS_VISIBLE) == 0) ? STATE_SYSTEM_INVISIBLE : 0;
 | |
|         pvarState->lVal |=
 | |
|             ((dwStyle & WS_DISABLED) > 0) ? STATE_SYSTEM_UNAVAILABLE : 0;
 | |
|       }
 | |
|     } else {
 | |
|       retCode = E_INVALIDARG;
 | |
|     }
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Check and returns Accessibility Shortcut if any for element
 | |
| STDMETHODIMP CefIAccessible::get_accKeyboardShortcut(
 | |
|     VARIANT varChild,
 | |
|     BSTR* pszKeyboardShortcut) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pszKeyboardShortcut && VALID_CHILDID(varChild))
 | |
|       *pszKeyboardShortcut = ::SysAllocString(L"None");
 | |
|     else
 | |
|       retCode = E_INVALIDARG;
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Return focused element from the accessibility tree
 | |
| STDMETHODIMP CefIAccessible::get_accFocus(VARIANT* pFocusChild) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     OsrAXNode* focusedNode = node_->GetAccessibilityHelper()->GetFocusedNode();
 | |
|     CefNativeAccessible* nativeObj = nullptr;
 | |
|     if (focusedNode)
 | |
|       nativeObj = focusedNode->GetNativeAccessibleObject(nullptr);
 | |
| 
 | |
|     if (nativeObj) {
 | |
|       if (nativeObj == this) {
 | |
|         pFocusChild->vt = VT_I4;
 | |
|         pFocusChild->lVal = CHILDID_SELF;
 | |
|       } else {
 | |
|         pFocusChild->vt = VT_DISPATCH;
 | |
|         pFocusChild->pdispVal = nativeObj;
 | |
|         pFocusChild->pdispVal->AddRef();
 | |
|       }
 | |
|     } else {
 | |
|       pFocusChild->vt = VT_EMPTY;
 | |
|     }
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Return a selection list for multiple selection items.
 | |
| STDMETHODIMP CefIAccessible::get_accSelection(VARIANT* pvarChildren) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pvarChildren)
 | |
|       pvarChildren->vt = VT_EMPTY;
 | |
|     else
 | |
|       retCode = E_INVALIDARG;
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Return a string description of the default action of our element, eg. push
 | |
| STDMETHODIMP CefIAccessible::get_accDefaultAction(VARIANT varChild,
 | |
|                                                   BSTR* pszDefaultAction) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pszDefaultAction && VALID_CHILDID(varChild))
 | |
|       *pszDefaultAction = ::SysAllocString(L"Push");
 | |
|     else
 | |
|       retCode = E_INVALIDARG;
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // child item selectionor for an item to take focus.
 | |
| STDMETHODIMP CefIAccessible::accSelect(long flagsSelect, VARIANT varChild) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (VALID_CHILDID(varChild)) {
 | |
|       HWND hwnd = node_->GetWindowHandle();
 | |
|       // we only support SELFLAG_TAKEFOCUS.
 | |
|       if (((flagsSelect & SELFLAG_TAKEFOCUS) > 0) && (GetFocus() == hwnd)) {
 | |
|         RECT rcWnd;
 | |
|         GetClientRect(hwnd, &rcWnd);
 | |
|         InvalidateRect(hwnd, &rcWnd, FALSE);
 | |
|       } else {
 | |
|         retCode = S_FALSE;
 | |
|       }
 | |
|     } else {
 | |
|       retCode = E_INVALIDARG;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Returns back the screen coordinates of our element or one of its childs
 | |
| STDMETHODIMP CefIAccessible::accLocation(long* pxLeft,
 | |
|                                          long* pyTop,
 | |
|                                          long* pcxWidth,
 | |
|                                          long* pcyHeight,
 | |
|                                          VARIANT varChild) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pxLeft && pyTop && pcxWidth && pcyHeight && VALID_CHILDID(varChild)) {
 | |
|       CefRect loc = node_->AxLocation();
 | |
|       RECT rcItem = {loc.x, loc.y, loc.x + loc.width, loc.y + loc.height};
 | |
|       HWND hwnd = node_->GetWindowHandle();
 | |
|       ClientToScreen(hwnd, &rcItem);
 | |
| 
 | |
|       *pxLeft = rcItem.left;
 | |
|       *pyTop = rcItem.top;
 | |
|       *pcxWidth = rcItem.right - rcItem.left;
 | |
|       *pcyHeight = rcItem.bottom - rcItem.top;
 | |
|     } else {
 | |
|       retCode = E_INVALIDARG;
 | |
|     }
 | |
|   }
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Allow clients to move the keyboard focus within the control
 | |
| // Deprecated
 | |
| STDMETHODIMP CefIAccessible::accNavigate(long navDir,
 | |
|                                          VARIANT varStart,
 | |
|                                          VARIANT* pvarEndUpAt) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| // Check if the coordinates provided are within our element or child items.
 | |
| STDMETHODIMP CefIAccessible::accHitTest(long xLeft,
 | |
|                                         long yTop,
 | |
|                                         VARIANT* pvarChild) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode)) {
 | |
|     if (pvarChild) {
 | |
|       pvarChild->vt = VT_EMPTY;
 | |
| 
 | |
|       CefRect loc = node_->AxLocation();
 | |
|       RECT rcItem = {loc.x, loc.y, loc.x + loc.width, loc.y + loc.height};
 | |
|       POINT pt = {xLeft, yTop};
 | |
| 
 | |
|       ClientToScreen(node_->GetWindowHandle(), &rcItem);
 | |
| 
 | |
|       if (PtInRect(&rcItem, pt)) {
 | |
|         pvarChild->vt = VT_I4;
 | |
|         pvarChild->lVal = 1;
 | |
|       }
 | |
|     } else {
 | |
|       retCode = E_INVALIDARG;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Forces the default action of our element. In simplest cases, send a click.
 | |
| STDMETHODIMP CefIAccessible::accDoDefaultAction(VARIANT varChild) {
 | |
|   HRESULT retCode = DATACHECK(node_);
 | |
|   if (SUCCEEDED(retCode) && VALID_CHILDID(varChild)) {
 | |
|     // doing our default action for out button is to simply click the button.
 | |
|     CefRefPtr<CefBrowser> browser = node_->GetBrowser();
 | |
|     if (browser) {
 | |
|       CefMouseEvent mouse_event;
 | |
|       const CefRect& rect = node_->AxLocation();
 | |
|       mouse_event.x = MiddleX(rect);
 | |
|       mouse_event.y = MiddleY(rect);
 | |
| 
 | |
|       mouse_event.modifiers = 0;
 | |
|       browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1);
 | |
|       browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1);
 | |
|     }
 | |
|   } else {
 | |
|     retCode = E_INVALIDARG;
 | |
|   }
 | |
| 
 | |
|   return retCode;
 | |
| }
 | |
| 
 | |
| // Set the name for an element in the accessibility tree
 | |
| STDMETHODIMP CefIAccessible::put_accName(VARIANT varChild, BSTR szName) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| // Set the value for an element in the accessibility tree
 | |
| STDMETHODIMP CefIAccessible::put_accValue(VARIANT varChild, BSTR szValue) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| // Return E_NOTIMPL as no help file/ topic
 | |
| STDMETHODIMP CefIAccessible::get_accHelp(VARIANT varChild, BSTR* pszHelp) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CefIAccessible::get_accHelpTopic(BSTR* pszHelpFile,
 | |
|                                               VARIANT varChild,
 | |
|                                               long* pidTopic) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| // IDispatch - We are not going to return E_NOTIMPL from IDispatch methods and
 | |
| // let Active Accessibility implement the IAccessible interface for them.
 | |
| STDMETHODIMP CefIAccessible::GetTypeInfoCount(unsigned int FAR* pctinfo) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CefIAccessible::GetTypeInfo(unsigned int iTInfo,
 | |
|                                          LCID lcid,
 | |
|                                          ITypeInfo FAR* FAR* ppTInfo) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CefIAccessible::GetIDsOfNames(REFIID riid,
 | |
|                                            OLECHAR FAR* FAR* rgszNames,
 | |
|                                            unsigned int cNames,
 | |
|                                            LCID lcid,
 | |
|                                            DISPID FAR* rgDispId) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CefIAccessible::Invoke(DISPID dispIdMember,
 | |
|                                     REFIID riid,
 | |
|                                     LCID lcid,
 | |
|                                     WORD wFlags,
 | |
|                                     DISPPARAMS FAR* pDispParams,
 | |
|                                     VARIANT FAR* pVarResult,
 | |
|                                     EXCEPINFO FAR* pExcepInfo,
 | |
|                                     unsigned int FAR* puArgErr) {
 | |
|   return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {
 | |
|   if (event_type == "focus") {
 | |
|     // Notify Screen Reader of focus change
 | |
|     ::NotifyWinEvent(EVENT_OBJECT_FOCUS, GetWindowHandle(), OBJID_CLIENT,
 | |
|                      node_id_);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void OsrAXNode::Destroy() {
 | |
|   CefIAccessible* ptr = static_cast<CefIAccessible*>(platform_accessibility_);
 | |
|   if (ptr)
 | |
|     ptr->MarkDestroyed();
 | |
|   platform_accessibility_ = nullptr;
 | |
| }
 | |
| 
 | |
| // Create and return NSAccessibility Implementation Object for Window
 | |
| CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
 | |
|   if (!platform_accessibility_) {
 | |
|     platform_accessibility_ = new CefIAccessible(this);
 | |
|     platform_accessibility_->AddRef();
 | |
|     SetParent(parent);
 | |
|   }
 | |
|   return platform_accessibility_;
 | |
| }
 | |
| 
 | |
| }  // namespace client
 | |
| 
 | |
| #else  // !defined(CEF_USE_ATL)
 | |
| 
 | |
| namespace client {
 | |
| 
 | |
| void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {}
 | |
| 
 | |
| void OsrAXNode::Destroy() {}
 | |
| 
 | |
| CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| }  // namespace client
 | |
| 
 | |
| #endif  // !defined(CEF_USE_ATL)
 |