platform/mac/accessibility/aria-multiline.html sometimes asserts in AccessibilityCont...
[WebKit-https.git] / Tools / DumpRenderTree / win / AccessibilityControllerWin.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2013 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "AccessibilityController.h"
28
29 #include "AccessibilityUIElement.h"
30 #include "DumpRenderTree.h"
31 #include "FrameLoadDelegate.h"
32 #include <JavaScriptCore/JSRetainPtr.h>
33 #include <JavaScriptCore/JSStringRef.h>
34 #include <JavaScriptCore/JSStringRefBSTR.h>
35 #include <WebCore/AccessibilityObjectWrapperWin.h>
36 #include <WebCore/COMPtr.h>
37 #include <WebKit/WebKit.h>
38 #include <comutil.h>
39 #include <oleacc.h>
40 #include <string>
41 #include <wtf/Assertions.h>
42 #include <wtf/text/AtomicString.h>
43
44 using namespace std;
45
46 AccessibilityController::AccessibilityController()
47     : m_focusEventHook(0)
48     , m_scrollingStartEventHook(0)
49     , m_valueChangeEventHook(0)
50     , m_allEventsHook(0)
51     , m_notificationsEventHook(0)
52 {
53 }
54
55 AccessibilityController::~AccessibilityController()
56 {
57     setLogFocusEvents(false);
58     setLogAccessibilityEvents(false);
59     setLogValueChangeEvents(false);
60
61     if (m_notificationsEventHook)
62         UnhookWinEvent(m_notificationsEventHook);
63
64     for (HashMap<PlatformUIElement, JSObjectRef>::iterator it = m_notificationListeners.begin(); it != m_notificationListeners.end(); ++it)
65         JSValueUnprotect(frame->globalContext(), it->value);
66 }
67
68 AccessibilityUIElement AccessibilityController::elementAtPoint(int x, int y)
69 {
70     // FIXME: implement
71     return 0;
72 }
73
74 static COMPtr<IAccessibleComparable> comparableObject(const COMPtr<IServiceProvider>& serviceProvider)
75 {
76     COMPtr<IAccessibleComparable> comparable;
77     serviceProvider->QueryService(SID_AccessibleComparable, __uuidof(IAccessibleComparable), reinterpret_cast<void**>(&comparable));
78     return comparable;
79 }
80
81 static COMPtr<IAccessible> findAccessibleObjectById(AccessibilityUIElement parentObject, BSTR idAttribute)
82 {
83     COMPtr<IAccessible> parentIAccessible = parentObject.platformUIElement();
84
85     COMPtr<IServiceProvider> serviceProvider(Query, parentIAccessible);
86     if (!serviceProvider)
87         return 0;
88
89     COMPtr<IAccessibleComparable> comparable = comparableObject(serviceProvider);
90     if (!comparable)
91         return 0;
92
93     VARIANT value;
94     ::VariantInit(&value);
95
96     _bstr_t elementIdAttributeKey(L"AXDRTElementIdAttribute");
97     if (SUCCEEDED(comparable->get_attribute(elementIdAttributeKey, &value))) {
98         ASSERT(V_VT(&value) == VT_BSTR);
99         if (VARCMP_EQ == ::VarBstrCmp(value.bstrVal, idAttribute, LOCALE_USER_DEFAULT, 0)) {
100             ::VariantClear(&value);
101             return parentIAccessible;
102         }
103     }
104     ::VariantClear(&value);
105
106     long childCount = parentObject.childrenCount();
107     if (!childCount)
108         return 0;
109
110     COMPtr<IAccessible> result;
111     for (long i = 0; i < childCount; ++i) {
112         AccessibilityUIElement childAtIndex = parentObject.getChildAtIndex(i);
113
114         result = findAccessibleObjectById(childAtIndex, idAttribute);
115         if (result)
116             return result;
117     }
118
119     return 0;
120 }
121
122 AccessibilityUIElement AccessibilityController::accessibleElementById(JSStringRef id)
123 {
124     AccessibilityUIElement rootAccessibilityUIElement = rootElement();
125
126     BSTR idAttribute = JSStringCopyBSTR(id);
127
128     COMPtr<IAccessible> result = findAccessibleObjectById(rootAccessibilityUIElement, idAttribute);
129
130     ::SysFreeString(idAttribute);
131
132     if (result)
133         return AccessibilityUIElement(result);
134
135     return 0;
136 }
137
138 AccessibilityUIElement AccessibilityController::focusedElement()
139 {
140     COMPtr<IAccessible> rootAccessible = rootElement().platformUIElement();
141
142     VARIANT vFocus;
143     if (FAILED(rootAccessible->get_accFocus(&vFocus)))
144         return 0;
145
146     if (V_VT(&vFocus) == VT_I4) {
147         ASSERT(V_I4(&vFocus) == CHILDID_SELF);
148         // The root accessible object is the focused object.
149         return rootAccessible;
150     }
151
152     ASSERT(V_VT(&vFocus) == VT_DISPATCH);
153     // We have an IDispatch; query for IAccessible.
154     return COMPtr<IAccessible>(Query, V_DISPATCH(&vFocus));
155 }
156
157 AccessibilityUIElement AccessibilityController::rootElement()
158 {
159     COMPtr<IWebView> view;
160     if (FAILED(frame->webView(&view)))
161         return 0;
162
163     COMPtr<IWebViewPrivate> viewPrivate(Query, view);
164     if (!viewPrivate)
165         return 0;
166
167     HWND webViewWindow;
168     if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
169         return 0;
170
171     // Get the root accessible object by querying for the accessible object for the
172     // WebView's window.
173     COMPtr<IAccessible> rootAccessible;
174     if (FAILED(AccessibleObjectFromWindow(webViewWindow, static_cast<DWORD>(OBJID_CLIENT), __uuidof(IAccessible), reinterpret_cast<void**>(&rootAccessible))))
175         return 0;
176
177     return rootAccessible;
178 }
179
180 static void CALLBACK logEventProc(HWINEVENTHOOK, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD, DWORD)
181 {
182     // Get the accessible object for this event.
183     COMPtr<IAccessible> parentObject;
184
185     VARIANT vChild;
186     VariantInit(&vChild);
187
188     HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &parentObject, &vChild);
189     ASSERT(SUCCEEDED(hr));
190
191     // Get the name of the focused element, and log it to stdout.
192     BSTR nameBSTR;
193     hr = parentObject->get_accName(vChild, &nameBSTR);
194     ASSERT(SUCCEEDED(hr));
195     wstring name(nameBSTR, ::SysStringLen(nameBSTR));
196     SysFreeString(nameBSTR);
197
198     switch (event) {
199         case EVENT_OBJECT_FOCUS:
200             printf("Received focus event for object '%S'.\n", name.c_str());
201             break;
202
203         case EVENT_OBJECT_SELECTION:
204             printf("Received selection event for object '%S'.\n", name.c_str());
205             break;
206
207         case EVENT_OBJECT_VALUECHANGE: {
208             BSTR valueBSTR;
209             hr = parentObject->get_accValue(vChild, &valueBSTR);
210             ASSERT(SUCCEEDED(hr));
211             wstring value(valueBSTR, ::SysStringLen(valueBSTR));
212             SysFreeString(valueBSTR);
213
214             printf("Received value change event for object '%S', value '%S'.\n", name.c_str(), value.c_str());
215             break;
216         }
217
218         case EVENT_SYSTEM_SCROLLINGSTART:
219             printf("Received scrolling start event for object '%S'.\n", name.c_str());
220             break;
221
222         default:
223             printf("Received unknown event for object '%S'.\n", name.c_str());
224             break;
225     }
226
227     VariantClear(&vChild);
228 }
229
230 void AccessibilityController::setLogFocusEvents(bool logFocusEvents)
231 {
232     if (!!m_focusEventHook == logFocusEvents)
233         return;
234
235     if (!logFocusEvents) {
236         UnhookWinEvent(m_focusEventHook);
237         m_focusEventHook = 0;
238         return;
239     }
240
241     // Ensure that accessibility is initialized for the WebView by querying for
242     // the root accessible object.
243     rootElement();
244
245     m_focusEventHook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, GetModuleHandle(0), logEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
246
247     ASSERT(m_focusEventHook);
248 }
249
250 void AccessibilityController::platformResetToConsistentState()
251 {
252 }
253
254 void AccessibilityController::setLogValueChangeEvents(bool logValueChangeEvents)
255 {
256     if (!!m_valueChangeEventHook == logValueChangeEvents)
257         return;
258
259     if (!logValueChangeEvents) {
260         UnhookWinEvent(m_valueChangeEventHook);
261         m_valueChangeEventHook = 0;
262         return;
263     }
264
265     // Ensure that accessibility is initialized for the WebView by querying for
266     // the root accessible object.
267     rootElement();
268
269     m_valueChangeEventHook = SetWinEventHook(EVENT_OBJECT_VALUECHANGE, EVENT_OBJECT_VALUECHANGE, GetModuleHandle(0), logEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
270
271     ASSERT(m_valueChangeEventHook);
272 }
273
274 void AccessibilityController::setLogScrollingStartEvents(bool logScrollingStartEvents)
275 {
276     if (!!m_scrollingStartEventHook == logScrollingStartEvents)
277         return;
278
279     if (!logScrollingStartEvents) {
280         UnhookWinEvent(m_scrollingStartEventHook);
281         m_scrollingStartEventHook = 0;
282         return;
283     }
284
285     // Ensure that accessibility is initialized for the WebView by querying for
286     // the root accessible object.
287     rootElement();
288
289     m_scrollingStartEventHook = SetWinEventHook(EVENT_SYSTEM_SCROLLINGSTART, EVENT_SYSTEM_SCROLLINGSTART, GetModuleHandle(0), logEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
290
291     ASSERT(m_scrollingStartEventHook);
292 }
293
294 void AccessibilityController::setLogAccessibilityEvents(bool logAccessibilityEvents)
295 {
296     if (!!m_allEventsHook == logAccessibilityEvents)
297         return;
298
299     if (!logAccessibilityEvents) {
300         UnhookWinEvent(m_allEventsHook);
301         m_allEventsHook = 0;
302         return;
303     }
304
305     // Ensure that accessibility is initialized for the WebView by querying for
306     // the root accessible object.
307     rootElement();
308
309     m_allEventsHook = SetWinEventHook(EVENT_MIN, EVENT_MAX, GetModuleHandle(0), logEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
310
311     ASSERT(m_allEventsHook);
312 }
313
314 static string stringEvent(DWORD event)
315 {
316     switch(event) {
317         case EVENT_OBJECT_VALUECHANGE:
318             return "value change event";
319         default:
320             return "unknown event";
321     }
322 }
323
324 static void CALLBACK notificationListenerProc(HWINEVENTHOOK, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD, DWORD)
325 {
326     // Get the accessible object for this event.
327     COMPtr<IAccessible> parentObject;
328
329     VARIANT vChild;
330     VariantInit(&vChild);
331
332     HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &parentObject, &vChild);
333     if (FAILED(hr) || !parentObject)
334         return;
335
336     COMPtr<IDispatch> childDispatch;
337     if (FAILED(parentObject->get_accChild(vChild, &childDispatch))) {
338         VariantClear(&vChild);
339         return;
340     }
341
342     COMPtr<IAccessible> childAccessible(Query, childDispatch);
343
344     sharedFrameLoadDelegate->accessibilityController()->winNotificationReceived(childAccessible, stringEvent(event));
345
346     VariantClear(&vChild);
347 }
348
349 bool AccessibilityController::addNotificationListener(JSObjectRef functionCallback)
350 {
351     return false;
352 }
353
354 void AccessibilityController::removeNotificationListener()
355 {
356 }
357
358 void AccessibilityController::winNotificationReceived(PlatformUIElement element, const string& eventName)
359 {
360     for (HashMap<PlatformUIElement, JSObjectRef>::iterator it = m_notificationListeners.begin(); it != m_notificationListeners.end(); ++it) {
361         COMPtr<IServiceProvider> thisServiceProvider(Query, it->key);
362         if (!thisServiceProvider)
363             continue;
364
365         COMPtr<IAccessibleComparable> thisComparable = comparableObject(thisServiceProvider);
366         if (!thisComparable)
367             continue;
368
369         COMPtr<IServiceProvider> elementServiceProvider(Query, element);
370         if (!elementServiceProvider)
371             continue;
372
373         COMPtr<IAccessibleComparable> elementComparable = comparableObject(elementServiceProvider);
374         if (!elementComparable)
375             continue;
376
377         BOOL isSame = FALSE;
378         thisComparable->isSameObject(elementComparable.get(), &isSame);
379         if (!isSame)
380             continue;
381
382         JSRetainPtr<JSStringRef> jsNotification(Adopt, JSStringCreateWithUTF8CString(eventName.c_str()));
383         JSValueRef argument = JSValueMakeString(frame->globalContext(), jsNotification.get());
384         JSObjectCallAsFunction(frame->globalContext(), it->value, 0, 1, &argument, 0);
385     }
386 }
387
388 void AccessibilityController::winAddNotificationListener(PlatformUIElement element, JSObjectRef functionCallback)
389 {
390     if (!m_notificationsEventHook)
391         m_notificationsEventHook = SetWinEventHook(EVENT_MIN, EVENT_MAX, GetModuleHandle(0), notificationListenerProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
392
393     JSValueProtect(frame->globalContext(), functionCallback);
394     m_notificationListeners.add(element, functionCallback);
395 }
396
397 JSRetainPtr<JSStringRef> AccessibilityController::platformName() const
398 {
399     JSRetainPtr<JSStringRef> platformName(Adopt, JSStringCreateWithUTF8CString("win"));
400     return platformName;
401 }