mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Implement accessibility enhancements (issue #1217)
- Add new CefBrowserHost::SetAccessibilityState method for toggling accessibility state when readers are detected by the client. - Add new CefAccessibilityHandler interface for the delivery of accessibility notifications to windowless (OSR) clients. - Fix delivery of CefFocusHandler callbacks to windowless clients. - cefclient: Add example windowless accessibility implementation on Windows and macOS. - cefclient: Automatically detect screen readers on Windows and macOS.
This commit is contained in:
committed by
Marshall Greenblatt
parent
64fcfa6068
commit
816f700d3e
674
tests/cefclient/browser/osr_accessibility_node_win.cc
Normal file
674
tests/cefclient/browser/osr_accessibility_node_win.cc
Normal file
@@ -0,0 +1,674 @@
|
||||
// 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 <string>
|
||||
#include <atlbase.h>
|
||||
#include <oleacc.h>
|
||||
|
||||
#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 == "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);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
//
|
||||
// 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);
|
||||
STDMETHODIMP GetTypeInfo(unsigned int iTInfo, LCID lcid,
|
||||
ITypeInfo FAR* FAR* ppTInfo);
|
||||
STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames,
|
||||
unsigned int cNames, LCID lcid,
|
||||
DISPID FAR* rgDispId);
|
||||
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
|
||||
DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult,
|
||||
EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr);
|
||||
|
||||
CefIAccessible(OsrAXNode* node) : node_(node), ref_count_(0) {
|
||||
}
|
||||
|
||||
// Remove the node reference when OsrAXNode is destroyed, so that
|
||||
// MSAA clients get CO_E_OBJNOTCONNECTED
|
||||
void MarkDestroyed() {
|
||||
node_ = NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
// 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 = NULL;
|
||||
|
||||
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 = NULL;
|
||||
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 == NULL)
|
||||
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::string 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::string 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::string 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 = NULL;
|
||||
if (focusedNode)
|
||||
nativeObj = focusedNode->GetNativeAccessibleObject(NULL);
|
||||
|
||||
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_ = NULL;
|
||||
}
|
||||
|
||||
// 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::Destroy() {
|
||||
}
|
||||
|
||||
CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
|
||||
#endif // !defined(CEF_USE_ATL)
|
Reference in New Issue
Block a user