1 // Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
2 // 2013 The Chromium Authors. All rights reserved. Use of this source code is
3 // governed by a BSD-style license that can be found in the LICENSE file.
4
5 // This class implements our accessible proxy object that handles moving
6 // data back and forth between MSAA clients and CefClient renderers.
7 // Sample implementation based on ui\accessibility\ax_platform_node_win.h
8
9 #include "tests/cefclient/browser/osr_accessibility_node.h"
10
11 #if defined(CEF_USE_ATL)
12
13 #include <atlbase.h>
14 #include <oleacc.h>
15 #include <string>
16
17 #include "tests/cefclient/browser/osr_accessibility_helper.h"
18
19 namespace client {
20
21 // Return CO_E_OBJNOTCONNECTED for accessible objects thar still exists but the
22 // window and/or object it references has been destroyed.
23 #define DATACHECK(node) (node) ? S_OK : CO_E_OBJNOTCONNECTED
24 #define VALID_CHILDID(varChild) ((varChild.vt == VT_I4))
25
26 namespace {
27
28 // Helper function to convert a rectangle from client coordinates to screen
29 // coordinates.
ClientToScreen(HWND hwnd,LPRECT lpRect)30 void ClientToScreen(HWND hwnd, LPRECT lpRect) {
31 if (lpRect) {
32 POINT ptTL = {lpRect->left, lpRect->top};
33 POINT ptBR = {lpRect->right, lpRect->bottom};
34 // Win32 API only provides the call for a point.
35 ClientToScreen(hwnd, &ptTL);
36 ClientToScreen(hwnd, &ptBR);
37 SetRect(lpRect, ptTL.x, ptTL.y, ptBR.x, ptBR.y);
38 }
39 }
40
41 // Helper function to convert to MSAARole
AxRoleToMSAARole(const std::string & role_string)42 int AxRoleToMSAARole(const std::string& role_string) {
43 if (role_string == "alert")
44 return ROLE_SYSTEM_ALERT;
45 if (role_string == "application")
46 return ROLE_SYSTEM_APPLICATION;
47 if (role_string == "buttonDropDown")
48 return ROLE_SYSTEM_BUTTONDROPDOWN;
49 if (role_string == "popUpButton")
50 return ROLE_SYSTEM_BUTTONMENU;
51 if (role_string == "checkBox")
52 return ROLE_SYSTEM_CHECKBUTTON;
53 if (role_string == "comboBox")
54 return ROLE_SYSTEM_COMBOBOX;
55 if (role_string == "dialog")
56 return ROLE_SYSTEM_DIALOG;
57 if (role_string == "genericContainer")
58 return ROLE_SYSTEM_GROUPING;
59 if (role_string == "group")
60 return ROLE_SYSTEM_GROUPING;
61 if (role_string == "image")
62 return ROLE_SYSTEM_GRAPHIC;
63 if (role_string == "link")
64 return ROLE_SYSTEM_LINK;
65 if (role_string == "locationBar")
66 return ROLE_SYSTEM_GROUPING;
67 if (role_string == "menuBar")
68 return ROLE_SYSTEM_MENUBAR;
69 if (role_string == "menuItem")
70 return ROLE_SYSTEM_MENUITEM;
71 if (role_string == "menuListPopup")
72 return ROLE_SYSTEM_MENUPOPUP;
73 if (role_string == "tree")
74 return ROLE_SYSTEM_OUTLINE;
75 if (role_string == "treeItem")
76 return ROLE_SYSTEM_OUTLINEITEM;
77 if (role_string == "tab")
78 return ROLE_SYSTEM_PAGETAB;
79 if (role_string == "tabList")
80 return ROLE_SYSTEM_PAGETABLIST;
81 if (role_string == "pane")
82 return ROLE_SYSTEM_PANE;
83 if (role_string == "progressIndicator")
84 return ROLE_SYSTEM_PROGRESSBAR;
85 if (role_string == "button")
86 return ROLE_SYSTEM_PUSHBUTTON;
87 if (role_string == "radioButton")
88 return ROLE_SYSTEM_RADIOBUTTON;
89 if (role_string == "scrollBar")
90 return ROLE_SYSTEM_SCROLLBAR;
91 if (role_string == "splitter")
92 return ROLE_SYSTEM_SEPARATOR;
93 if (role_string == "slider")
94 return ROLE_SYSTEM_SLIDER;
95 if (role_string == "staticText")
96 return ROLE_SYSTEM_STATICTEXT;
97 if (role_string == "textField")
98 return ROLE_SYSTEM_TEXT;
99 if (role_string == "titleBar")
100 return ROLE_SYSTEM_TITLEBAR;
101 if (role_string == "toolbar")
102 return ROLE_SYSTEM_TOOLBAR;
103 if (role_string == "webView")
104 return ROLE_SYSTEM_GROUPING;
105 if (role_string == "window")
106 return ROLE_SYSTEM_WINDOW;
107 if (role_string == "client")
108 return ROLE_SYSTEM_CLIENT;
109 // This is the default role for MSAA.
110 return ROLE_SYSTEM_CLIENT;
111 }
112
MiddleX(const CefRect & rect)113 static inline int MiddleX(const CefRect& rect) {
114 return rect.x + rect.width / 2;
115 }
116
MiddleY(const CefRect & rect)117 static inline int MiddleY(const CefRect& rect) {
118 return rect.y + rect.height / 2;
119 }
120
121 } // namespace
122
123 struct CefIAccessible : public IAccessible {
124 public:
125 // Implement IUnknown
126 STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override;
127 STDMETHODIMP_(ULONG) AddRef() override;
128 STDMETHODIMP_(ULONG) Release() override;
129
130 //
131 // IAccessible methods.
132 //
133 // Retrieves the child element or child object at a given point on the screen.
134 STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child) override;
135
136 // Performs the object's default action.
137 STDMETHODIMP accDoDefaultAction(VARIANT var_id) override;
138
139 // Retrieves the specified object's current screen location.
140 STDMETHODIMP accLocation(LONG* x_left,
141 LONG* y_top,
142 LONG* width,
143 LONG* height,
144 VARIANT var_id) override;
145
146 // Traverses to another UI element and retrieves the object.
147 STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start, VARIANT* end) override;
148
149 // Retrieves an IDispatch interface pointer for the specified child.
150 STDMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child) override;
151
152 // Retrieves the number of accessible children.
153 STDMETHODIMP get_accChildCount(LONG* child_count) override;
154
155 // Retrieves a string that describes the object's default action.
156 STDMETHODIMP get_accDefaultAction(VARIANT var_id,
157 BSTR* default_action) override;
158
159 // Retrieves the tooltip description.
160 STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override;
161
162 // Retrieves the object that has the keyboard focus.
163 STDMETHODIMP get_accFocus(VARIANT* focus_child) override;
164
165 // Retrieves the specified object's shortcut.
166 STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id,
167 BSTR* access_key) override;
168
169 // Retrieves the name of the specified object.
170 STDMETHODIMP get_accName(VARIANT var_id, BSTR* name) override;
171
172 // Retrieves the IDispatch interface of the object's parent.
173 STDMETHODIMP get_accParent(IDispatch** disp_parent) override;
174
175 // Retrieves information describing the role of the specified object.
176 STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role) override;
177
178 // Retrieves the current state of the specified object.
179 STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state) override;
180
181 // Gets the help string for the specified object.
182 STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help) override;
183
184 // Retrieve or set the string value associated with the specified object.
185 // Setting the value is not typically used by screen readers, but it's
186 // used frequently by automation software.
187 STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override;
188 STDMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override;
189
190 // IAccessible methods not implemented.
191 STDMETHODIMP get_accSelection(VARIANT* selected) override;
192 STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id) override;
193 STDMETHODIMP get_accHelpTopic(BSTR* help_file,
194 VARIANT var_id,
195 LONG* topic_id) override;
196 STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override;
197
198 // Implement IDispatch
199 STDMETHODIMP GetTypeInfoCount(unsigned int FAR* pctinfo) override;
200 STDMETHODIMP GetTypeInfo(unsigned int iTInfo,
201 LCID lcid,
202 ITypeInfo FAR* FAR* ppTInfo) override;
203 STDMETHODIMP GetIDsOfNames(REFIID riid,
204 OLECHAR FAR* FAR* rgszNames,
205 unsigned int cNames,
206 LCID lcid,
207 DISPID FAR* rgDispId) override;
208 STDMETHODIMP Invoke(DISPID dispIdMember,
209 REFIID riid,
210 LCID lcid,
211 WORD wFlags,
212 DISPPARAMS FAR* pDispParams,
213 VARIANT FAR* pVarResult,
214 EXCEPINFO FAR* pExcepInfo,
215 unsigned int FAR* puArgErr) override;
216
CefIAccessibleclient::CefIAccessible217 CefIAccessible(OsrAXNode* node) : ref_count_(0), node_(node) {}
218
219 // Remove the node reference when OsrAXNode is destroyed, so that
220 // MSAA clients get CO_E_OBJNOTCONNECTED
MarkDestroyedclient::CefIAccessible221 void MarkDestroyed() { node_ = nullptr; }
222
223 protected:
~CefIAccessibleclient::CefIAccessible224 virtual ~CefIAccessible() {}
225
226 // Ref Count
227 ULONG ref_count_;
228 // OsrAXNode* proxy object
229 OsrAXNode* node_;
230 };
231
232 // Implement IUnknown
233 // *********************
234
235 // Handles ref counting and querying for other supported interfaces.
236 // We only support, IUnknown, IDispatch and IAccessible.
QueryInterface(REFIID riid,void ** ppvObject)237 STDMETHODIMP CefIAccessible::QueryInterface(REFIID riid, void** ppvObject) {
238 if (riid == IID_IAccessible)
239 *ppvObject = static_cast<IAccessible*>(this);
240 else if (riid == IID_IDispatch)
241 *ppvObject = static_cast<IDispatch*>(this);
242 else if (riid == IID_IUnknown)
243 *ppvObject = static_cast<IUnknown*>(this);
244 else
245 *ppvObject = nullptr;
246
247 if (*ppvObject)
248 reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
249
250 return (*ppvObject) ? S_OK : E_NOINTERFACE;
251 }
252
253 // Increments COM objects refcount required by IUnknown for reference counting
STDMETHODIMP_(ULONG)254 STDMETHODIMP_(ULONG) CefIAccessible::AddRef() {
255 return InterlockedIncrement((LONG volatile*)&ref_count_);
256 }
257
STDMETHODIMP_(ULONG)258 STDMETHODIMP_(ULONG) CefIAccessible::Release() {
259 ULONG ulRefCnt = InterlockedDecrement((LONG volatile*)&ref_count_);
260 if (ulRefCnt == 0) {
261 // Remove reference from OsrAXNode
262 if (node_)
263 node_->Destroy();
264 delete this;
265 }
266
267 return ulRefCnt;
268 }
269
270 // Implement IAccessible
271 // *********************
272
273 // Returns the parent IAccessible in the form of an IDispatch interface.
get_accParent(IDispatch ** ppdispParent)274 STDMETHODIMP CefIAccessible::get_accParent(IDispatch** ppdispParent) {
275 HRESULT retCode = DATACHECK(node_);
276 if (SUCCEEDED(retCode)) {
277 if (ppdispParent) {
278 CefNativeAccessible* parent = node_->GetParentAccessibleObject();
279 if (!parent) {
280 // Find our parent window
281 HWND hWnd = ::GetParent(node_->GetWindowHandle());
282 // if we have a window attempt to get its IAccessible pointer
283 if (hWnd) {
284 AccessibleObjectFromWindow(hWnd, (DWORD)OBJID_CLIENT, IID_IAccessible,
285 (void**)(&parent));
286 }
287 }
288
289 if (parent)
290 parent->AddRef();
291 *ppdispParent = parent;
292 retCode = (*ppdispParent) ? S_OK : S_FALSE;
293 }
294 } else {
295 retCode = E_INVALIDARG;
296 }
297 return retCode;
298 }
299
300 // Returns the number of children we have for this element.
get_accChildCount(long * pcountChildren)301 STDMETHODIMP CefIAccessible::get_accChildCount(long* pcountChildren) {
302 HRESULT retCode = DATACHECK(node_);
303 if (SUCCEEDED(retCode) && pcountChildren) {
304 // Get Child node count for this from Accessibility tree
305 *pcountChildren = node_->GetChildCount();
306 } else {
307 retCode = E_INVALIDARG;
308 }
309 return retCode;
310 }
311
312 // Returns a child IAccessible object.
get_accChild(VARIANT varChild,IDispatch ** ppdispChild)313 STDMETHODIMP CefIAccessible::get_accChild(VARIANT varChild,
314 IDispatch** ppdispChild) {
315 HRESULT retCode = DATACHECK(node_);
316 if (SUCCEEDED(retCode)) {
317 int numChilds = node_->GetChildCount();
318 // Mark Leaf node if there are no child
319 if (numChilds <= 0) {
320 *ppdispChild = nullptr;
321 return S_FALSE;
322 } else {
323 if (ppdispChild && VALID_CHILDID(varChild)) {
324 if (varChild.lVal == CHILDID_SELF) {
325 *ppdispChild = this;
326 } else {
327 // Convert to 0 based index and get Child Node.
328 OsrAXNode* child = node_->ChildAtIndex(varChild.lVal - 1);
329 // Fallback to focused node
330 if (!child)
331 child = node_->GetAccessibilityHelper()->GetFocusedNode();
332
333 *ppdispChild = child->GetNativeAccessibleObject(node_);
334 }
335 if (*ppdispChild == nullptr)
336 retCode = S_FALSE;
337 else
338 (*ppdispChild)->AddRef();
339 }
340 }
341 }
342 return retCode;
343 }
344
345 // Check and returns the accessible name for element from accessibility tree
get_accName(VARIANT varChild,BSTR * pszName)346 STDMETHODIMP CefIAccessible::get_accName(VARIANT varChild, BSTR* pszName) {
347 HRESULT retCode = DATACHECK(node_);
348 if (SUCCEEDED(retCode)) {
349 if (pszName && VALID_CHILDID(varChild)) {
350 std::wstring name = node_->AxName();
351 CComBSTR bstrResult(name.c_str());
352 *pszName = bstrResult.Detach();
353 }
354 } else {
355 retCode = E_INVALIDARG;
356 }
357 return retCode;
358 }
359
360 // Check and returns the value for element from accessibility tree
get_accValue(VARIANT varChild,BSTR * pszValue)361 STDMETHODIMP CefIAccessible::get_accValue(VARIANT varChild, BSTR* pszValue) {
362 HRESULT retCode = DATACHECK(node_);
363 if (SUCCEEDED(retCode)) {
364 if (pszValue && VALID_CHILDID(varChild)) {
365 std::wstring name = node_->AxValue();
366 CComBSTR bstrResult(name.c_str());
367 *pszValue = bstrResult.Detach();
368 }
369 } else {
370 retCode = E_INVALIDARG;
371 }
372 return retCode;
373 }
374
375 // Check and returns the description for element from accessibility tree
get_accDescription(VARIANT varChild,BSTR * pszDescription)376 STDMETHODIMP CefIAccessible::get_accDescription(VARIANT varChild,
377 BSTR* pszDescription) {
378 HRESULT retCode = DATACHECK(node_);
379 if (SUCCEEDED(retCode)) {
380 if (pszDescription && VALID_CHILDID(varChild)) {
381 std::wstring name = node_->AxDescription();
382 CComBSTR bstrResult(name.c_str());
383 *pszDescription = bstrResult.Detach();
384 }
385 } else {
386 retCode = E_INVALIDARG;
387 }
388 return retCode;
389 }
390
391 // Check and returns the MSAA Role for element from accessibility tree
get_accRole(VARIANT varChild,VARIANT * pvarRole)392 STDMETHODIMP CefIAccessible::get_accRole(VARIANT varChild, VARIANT* pvarRole) {
393 HRESULT retCode = DATACHECK(node_);
394 if (SUCCEEDED(retCode)) {
395 // Get the accessibilty role and Map to MSAA Role
396 if (pvarRole) {
397 pvarRole->vt = VT_I4;
398 pvarRole->lVal = AxRoleToMSAARole(node_->AxRole());
399 } else {
400 retCode = E_INVALIDARG;
401 }
402 }
403 return retCode;
404 }
405
406 // Check and returns Accessibility State for element from accessibility tree
get_accState(VARIANT varChild,VARIANT * pvarState)407 STDMETHODIMP CefIAccessible::get_accState(VARIANT varChild,
408 VARIANT* pvarState) {
409 HRESULT retCode = DATACHECK(node_);
410 if (SUCCEEDED(retCode)) {
411 if (pvarState) {
412 pvarState->vt = VT_I4;
413 pvarState->lVal =
414 (GetFocus() == node_->GetWindowHandle()) ? STATE_SYSTEM_FOCUSED : 0;
415 pvarState->lVal |= STATE_SYSTEM_PRESSED;
416 pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
417
418 // For child
419 if (varChild.lVal == CHILDID_SELF) {
420 DWORD dwStyle = GetWindowLong(node_->GetWindowHandle(), GWL_STYLE);
421 pvarState->lVal |=
422 ((dwStyle & WS_VISIBLE) == 0) ? STATE_SYSTEM_INVISIBLE : 0;
423 pvarState->lVal |=
424 ((dwStyle & WS_DISABLED) > 0) ? STATE_SYSTEM_UNAVAILABLE : 0;
425 }
426 } else {
427 retCode = E_INVALIDARG;
428 }
429 }
430 return retCode;
431 }
432
433 // Check and returns Accessibility Shortcut if any for element
get_accKeyboardShortcut(VARIANT varChild,BSTR * pszKeyboardShortcut)434 STDMETHODIMP CefIAccessible::get_accKeyboardShortcut(
435 VARIANT varChild,
436 BSTR* pszKeyboardShortcut) {
437 HRESULT retCode = DATACHECK(node_);
438 if (SUCCEEDED(retCode)) {
439 if (pszKeyboardShortcut && VALID_CHILDID(varChild))
440 *pszKeyboardShortcut = ::SysAllocString(L"None");
441 else
442 retCode = E_INVALIDARG;
443 }
444 return retCode;
445 }
446
447 // Return focused element from the accessibility tree
get_accFocus(VARIANT * pFocusChild)448 STDMETHODIMP CefIAccessible::get_accFocus(VARIANT* pFocusChild) {
449 HRESULT retCode = DATACHECK(node_);
450 if (SUCCEEDED(retCode)) {
451 OsrAXNode* focusedNode = node_->GetAccessibilityHelper()->GetFocusedNode();
452 CefNativeAccessible* nativeObj = nullptr;
453 if (focusedNode)
454 nativeObj = focusedNode->GetNativeAccessibleObject(nullptr);
455
456 if (nativeObj) {
457 if (nativeObj == this) {
458 pFocusChild->vt = VT_I4;
459 pFocusChild->lVal = CHILDID_SELF;
460 } else {
461 pFocusChild->vt = VT_DISPATCH;
462 pFocusChild->pdispVal = nativeObj;
463 pFocusChild->pdispVal->AddRef();
464 }
465 } else {
466 pFocusChild->vt = VT_EMPTY;
467 }
468 }
469 return retCode;
470 }
471
472 // Return a selection list for multiple selection items.
get_accSelection(VARIANT * pvarChildren)473 STDMETHODIMP CefIAccessible::get_accSelection(VARIANT* pvarChildren) {
474 HRESULT retCode = DATACHECK(node_);
475 if (SUCCEEDED(retCode)) {
476 if (pvarChildren)
477 pvarChildren->vt = VT_EMPTY;
478 else
479 retCode = E_INVALIDARG;
480 }
481 return retCode;
482 }
483
484 // Return a string description of the default action of our element, eg. push
get_accDefaultAction(VARIANT varChild,BSTR * pszDefaultAction)485 STDMETHODIMP CefIAccessible::get_accDefaultAction(VARIANT varChild,
486 BSTR* pszDefaultAction) {
487 HRESULT retCode = DATACHECK(node_);
488 if (SUCCEEDED(retCode)) {
489 if (pszDefaultAction && VALID_CHILDID(varChild))
490 *pszDefaultAction = ::SysAllocString(L"Push");
491 else
492 retCode = E_INVALIDARG;
493 }
494 return retCode;
495 }
496
497 // child item selectionor for an item to take focus.
accSelect(long flagsSelect,VARIANT varChild)498 STDMETHODIMP CefIAccessible::accSelect(long flagsSelect, VARIANT varChild) {
499 HRESULT retCode = DATACHECK(node_);
500 if (SUCCEEDED(retCode)) {
501 if (VALID_CHILDID(varChild)) {
502 HWND hwnd = node_->GetWindowHandle();
503 // we only support SELFLAG_TAKEFOCUS.
504 if (((flagsSelect & SELFLAG_TAKEFOCUS) > 0) && (GetFocus() == hwnd)) {
505 RECT rcWnd;
506 GetClientRect(hwnd, &rcWnd);
507 InvalidateRect(hwnd, &rcWnd, FALSE);
508 } else {
509 retCode = S_FALSE;
510 }
511 } else {
512 retCode = E_INVALIDARG;
513 }
514 }
515
516 return retCode;
517 }
518
519 // Returns back the screen coordinates of our element or one of its childs
accLocation(long * pxLeft,long * pyTop,long * pcxWidth,long * pcyHeight,VARIANT varChild)520 STDMETHODIMP CefIAccessible::accLocation(long* pxLeft,
521 long* pyTop,
522 long* pcxWidth,
523 long* pcyHeight,
524 VARIANT varChild) {
525 HRESULT retCode = DATACHECK(node_);
526 if (SUCCEEDED(retCode)) {
527 if (pxLeft && pyTop && pcxWidth && pcyHeight && VALID_CHILDID(varChild)) {
528 CefRect loc = node_->AxLocation();
529 RECT rcItem = {loc.x, loc.y, loc.x + loc.width, loc.y + loc.height};
530 HWND hwnd = node_->GetWindowHandle();
531 ClientToScreen(hwnd, &rcItem);
532
533 *pxLeft = rcItem.left;
534 *pyTop = rcItem.top;
535 *pcxWidth = rcItem.right - rcItem.left;
536 *pcyHeight = rcItem.bottom - rcItem.top;
537 } else {
538 retCode = E_INVALIDARG;
539 }
540 }
541 return retCode;
542 }
543
544 // Allow clients to move the keyboard focus within the control
545 // Deprecated
accNavigate(long navDir,VARIANT varStart,VARIANT * pvarEndUpAt)546 STDMETHODIMP CefIAccessible::accNavigate(long navDir,
547 VARIANT varStart,
548 VARIANT* pvarEndUpAt) {
549 return E_NOTIMPL;
550 }
551
552 // Check if the coordinates provided are within our element or child items.
accHitTest(long xLeft,long yTop,VARIANT * pvarChild)553 STDMETHODIMP CefIAccessible::accHitTest(long xLeft,
554 long yTop,
555 VARIANT* pvarChild) {
556 HRESULT retCode = DATACHECK(node_);
557 if (SUCCEEDED(retCode)) {
558 if (pvarChild) {
559 pvarChild->vt = VT_EMPTY;
560
561 CefRect loc = node_->AxLocation();
562 RECT rcItem = {loc.x, loc.y, loc.x + loc.width, loc.y + loc.height};
563 POINT pt = {xLeft, yTop};
564
565 ClientToScreen(node_->GetWindowHandle(), &rcItem);
566
567 if (PtInRect(&rcItem, pt)) {
568 pvarChild->vt = VT_I4;
569 pvarChild->lVal = 1;
570 }
571 } else {
572 retCode = E_INVALIDARG;
573 }
574 }
575
576 return retCode;
577 }
578
579 // Forces the default action of our element. In simplest cases, send a click.
accDoDefaultAction(VARIANT varChild)580 STDMETHODIMP CefIAccessible::accDoDefaultAction(VARIANT varChild) {
581 HRESULT retCode = DATACHECK(node_);
582 if (SUCCEEDED(retCode) && VALID_CHILDID(varChild)) {
583 // doing our default action for out button is to simply click the button.
584 CefRefPtr<CefBrowser> browser = node_->GetBrowser();
585 if (browser) {
586 CefMouseEvent mouse_event;
587 const CefRect& rect = node_->AxLocation();
588 mouse_event.x = MiddleX(rect);
589 mouse_event.y = MiddleY(rect);
590
591 mouse_event.modifiers = 0;
592 browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1);
593 browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1);
594 }
595 } else {
596 retCode = E_INVALIDARG;
597 }
598
599 return retCode;
600 }
601
602 // Set the name for an element in the accessibility tree
put_accName(VARIANT varChild,BSTR szName)603 STDMETHODIMP CefIAccessible::put_accName(VARIANT varChild, BSTR szName) {
604 return E_NOTIMPL;
605 }
606
607 // Set the value for an element in the accessibility tree
put_accValue(VARIANT varChild,BSTR szValue)608 STDMETHODIMP CefIAccessible::put_accValue(VARIANT varChild, BSTR szValue) {
609 return E_NOTIMPL;
610 }
611
612 // Return E_NOTIMPL as no help file/ topic
get_accHelp(VARIANT varChild,BSTR * pszHelp)613 STDMETHODIMP CefIAccessible::get_accHelp(VARIANT varChild, BSTR* pszHelp) {
614 return E_NOTIMPL;
615 }
616
get_accHelpTopic(BSTR * pszHelpFile,VARIANT varChild,long * pidTopic)617 STDMETHODIMP CefIAccessible::get_accHelpTopic(BSTR* pszHelpFile,
618 VARIANT varChild,
619 long* pidTopic) {
620 return E_NOTIMPL;
621 }
622
623 // IDispatch - We are not going to return E_NOTIMPL from IDispatch methods and
624 // let Active Accessibility implement the IAccessible interface for them.
GetTypeInfoCount(unsigned int FAR * pctinfo)625 STDMETHODIMP CefIAccessible::GetTypeInfoCount(unsigned int FAR* pctinfo) {
626 return E_NOTIMPL;
627 }
628
GetTypeInfo(unsigned int iTInfo,LCID lcid,ITypeInfo FAR * FAR * ppTInfo)629 STDMETHODIMP CefIAccessible::GetTypeInfo(unsigned int iTInfo,
630 LCID lcid,
631 ITypeInfo FAR* FAR* ppTInfo) {
632 return E_NOTIMPL;
633 }
634
GetIDsOfNames(REFIID riid,OLECHAR FAR * FAR * rgszNames,unsigned int cNames,LCID lcid,DISPID FAR * rgDispId)635 STDMETHODIMP CefIAccessible::GetIDsOfNames(REFIID riid,
636 OLECHAR FAR* FAR* rgszNames,
637 unsigned int cNames,
638 LCID lcid,
639 DISPID FAR* rgDispId) {
640 return E_NOTIMPL;
641 }
642
Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS FAR * pDispParams,VARIANT FAR * pVarResult,EXCEPINFO FAR * pExcepInfo,unsigned int FAR * puArgErr)643 STDMETHODIMP CefIAccessible::Invoke(DISPID dispIdMember,
644 REFIID riid,
645 LCID lcid,
646 WORD wFlags,
647 DISPPARAMS FAR* pDispParams,
648 VARIANT FAR* pVarResult,
649 EXCEPINFO FAR* pExcepInfo,
650 unsigned int FAR* puArgErr) {
651 return E_NOTIMPL;
652 }
653
NotifyAccessibilityEvent(std::string event_type) const654 void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {
655 if (event_type == "focus") {
656 // Notify Screen Reader of focus change
657 ::NotifyWinEvent(EVENT_OBJECT_FOCUS, GetWindowHandle(), OBJID_CLIENT,
658 node_id_);
659 }
660 }
661
Destroy()662 void OsrAXNode::Destroy() {
663 CefIAccessible* ptr = static_cast<CefIAccessible*>(platform_accessibility_);
664 if (ptr)
665 ptr->MarkDestroyed();
666 platform_accessibility_ = nullptr;
667 }
668
669 // Create and return NSAccessibility Implementation Object for Window
GetNativeAccessibleObject(OsrAXNode * parent)670 CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
671 if (!platform_accessibility_) {
672 platform_accessibility_ = new CefIAccessible(this);
673 platform_accessibility_->AddRef();
674 SetParent(parent);
675 }
676 return platform_accessibility_;
677 }
678
679 } // namespace client
680
681 #else // !defined(CEF_USE_ATL)
682
683 namespace client {
684
NotifyAccessibilityEvent(std::string event_type) const685 void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {}
686
Destroy()687 void OsrAXNode::Destroy() {}
688
GetNativeAccessibleObject(OsrAXNode * parent)689 CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
690 return nullptr;
691 }
692
693 } // namespace client
694
695 #endif // !defined(CEF_USE_ATL)
696