| // 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) |