diff -r 000000000000 -r 4f2f89ce4247 WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AccessibilityController.h" + +#include "AccessibilityUIElement.h" +#include "DumpRenderTree.h" +#include "FrameLoadDelegate.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +AccessibilityController::AccessibilityController() + : m_focusEventHook(0) + , m_scrollingStartEventHook(0) + , m_valueChangeEventHook(0) + , m_allEventsHook(0) +{ +} + +AccessibilityController::~AccessibilityController() +{ + setLogFocusEvents(false); + setLogValueChangeEvents(false); + + if (m_allEventsHook) + UnhookWinEvent(m_allEventsHook); + + for (HashMap::iterator it = m_notificationListeners.begin(); it != m_notificationListeners.end(); ++it) + JSValueUnprotect(frame->globalContext(), it->second); +} + +AccessibilityUIElement AccessibilityController::elementAtPoint(int x, int y) +{ + // FIXME: implement + return 0; +} + +AccessibilityUIElement AccessibilityController::focusedElement() +{ + COMPtr rootAccessible = rootElement().platformUIElement(); + + VARIANT vFocus; + if (FAILED(rootAccessible->get_accFocus(&vFocus))) + return 0; + + if (V_VT(&vFocus) == VT_I4) { + ASSERT(V_I4(&vFocus) == CHILDID_SELF); + // The root accessible object is the focused object. + return rootAccessible; + } + + ASSERT(V_VT(&vFocus) == VT_DISPATCH); + // We have an IDispatch; query for IAccessible. + return COMPtr(Query, V_DISPATCH(&vFocus)); +} + +AccessibilityUIElement AccessibilityController::rootElement() +{ + COMPtr view; + if (FAILED(frame->webView(&view))) + return 0; + + COMPtr viewPrivate(Query, view); + if (!viewPrivate) + return 0; + + HWND webViewWindow; + if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow))) + return 0; + + // Get the root accessible object by querying for the accessible object for the + // WebView's window. + COMPtr rootAccessible; + if (FAILED(AccessibleObjectFromWindow(webViewWindow, static_cast(OBJID_CLIENT), __uuidof(IAccessible), reinterpret_cast(&rootAccessible)))) + return 0; + + return rootAccessible; +} + +static void CALLBACK logEventProc(HWINEVENTHOOK, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD, DWORD) +{ + // Get the accessible object for this event. + COMPtr parentObject; + + VARIANT vChild; + VariantInit(&vChild); + + HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &parentObject, &vChild); + ASSERT(SUCCEEDED(hr)); + + // Get the name of the focused element, and log it to stdout. + BSTR nameBSTR; + hr = parentObject->get_accName(vChild, &nameBSTR); + ASSERT(SUCCEEDED(hr)); + wstring name(nameBSTR, ::SysStringLen(nameBSTR)); + SysFreeString(nameBSTR); + + switch (event) { + case EVENT_OBJECT_FOCUS: + printf("Received focus event for object '%S'.\n", name.c_str()); + break; + + case EVENT_OBJECT_VALUECHANGE: { + BSTR valueBSTR; + hr = parentObject->get_accValue(vChild, &valueBSTR); + ASSERT(SUCCEEDED(hr)); + wstring value(valueBSTR, ::SysStringLen(valueBSTR)); + SysFreeString(valueBSTR); + + printf("Received value change event for object '%S', value '%S'.\n", name.c_str(), value.c_str()); + break; + } + + case EVENT_SYSTEM_SCROLLINGSTART: + printf("Received scrolling start event for object '%S'.\n", name.c_str()); + break; + + default: + printf("Received unknown event for object '%S'.\n", name.c_str()); + break; + } + + VariantClear(&vChild); +} + +void AccessibilityController::setLogFocusEvents(bool logFocusEvents) +{ + if (!!m_focusEventHook == logFocusEvents) + return; + + if (!logFocusEvents) { + UnhookWinEvent(m_focusEventHook); + m_focusEventHook = 0; + return; + } + + // Ensure that accessibility is initialized for the WebView by querying for + // the root accessible object. + rootElement(); + + m_focusEventHook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, GetModuleHandle(0), logEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); + + ASSERT(m_focusEventHook); +} + +void AccessibilityController::setLogValueChangeEvents(bool logValueChangeEvents) +{ + if (!!m_valueChangeEventHook == logValueChangeEvents) + return; + + if (!logValueChangeEvents) { + UnhookWinEvent(m_valueChangeEventHook); + m_valueChangeEventHook = 0; + return; + } + + // Ensure that accessibility is initialized for the WebView by querying for + // the root accessible object. + rootElement(); + + m_valueChangeEventHook = SetWinEventHook(EVENT_OBJECT_VALUECHANGE, EVENT_OBJECT_VALUECHANGE, GetModuleHandle(0), logEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); + + ASSERT(m_valueChangeEventHook); +} + +void AccessibilityController::setLogScrollingStartEvents(bool logScrollingStartEvents) +{ + if (!!m_scrollingStartEventHook == logScrollingStartEvents) + return; + + if (!logScrollingStartEvents) { + UnhookWinEvent(m_scrollingStartEventHook); + m_scrollingStartEventHook = 0; + return; + } + + // Ensure that accessibility is initialized for the WebView by querying for + // the root accessible object. + rootElement(); + + m_scrollingStartEventHook = SetWinEventHook(EVENT_SYSTEM_SCROLLINGSTART, EVENT_SYSTEM_SCROLLINGSTART, GetModuleHandle(0), logEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); + + ASSERT(m_scrollingStartEventHook); +} + +static string stringEvent(DWORD event) +{ + switch(event) { + case EVENT_OBJECT_VALUECHANGE: + return "value change event"; + default: + return "unknown event"; + } +} + +static void CALLBACK notificationListenerProc(HWINEVENTHOOK, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD, DWORD) +{ + // Get the accessible object for this event. + COMPtr parentObject; + + VARIANT vChild; + VariantInit(&vChild); + + HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &parentObject, &vChild); + ASSERT(SUCCEEDED(hr)); + + COMPtr childDispatch; + if (FAILED(parentObject->get_accChild(vChild, &childDispatch))) { + VariantClear(&vChild); + return; + } + + COMPtr childAccessible(Query, childDispatch); + + sharedFrameLoadDelegate->accessibilityController()->notificationReceived(childAccessible, stringEvent(event)); + + VariantClear(&vChild); +} + +static COMPtr comparableObject(const COMPtr& serviceProvider) +{ + COMPtr comparable; + serviceProvider->QueryService(SID_AccessibleComparable, __uuidof(IAccessibleComparable), reinterpret_cast(&comparable)); + return comparable; +} + +void AccessibilityController::notificationReceived(PlatformUIElement element, const string& eventName) +{ + for (HashMap::iterator it = m_notificationListeners.begin(); it != m_notificationListeners.end(); ++it) { + COMPtr thisServiceProvider(Query, it->first); + if (!thisServiceProvider) + continue; + + COMPtr thisComparable = comparableObject(thisServiceProvider); + if (!thisComparable) + continue; + + COMPtr elementServiceProvider(Query, element); + if (!elementServiceProvider) + continue; + + COMPtr elementComparable = comparableObject(elementServiceProvider); + if (!elementComparable) + continue; + + BOOL isSame = FALSE; + thisComparable->isSameObject(elementComparable.get(), &isSame); + if (!isSame) + continue; + + JSRetainPtr jsNotification(Adopt, JSStringCreateWithUTF8CString(eventName.c_str())); + JSValueRef argument = JSValueMakeString(frame->globalContext(), jsNotification.get()); + JSObjectCallAsFunction(frame->globalContext(), it->second, NULL, 1, &argument, NULL); + } +} + +void AccessibilityController::addNotificationListener(PlatformUIElement element, JSObjectRef functionCallback) +{ + if (!m_allEventsHook) + m_allEventsHook = SetWinEventHook(EVENT_MIN, EVENT_MAX, GetModuleHandle(0), notificationListenerProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); + + JSValueProtect(frame->globalContext(), functionCallback); + m_notificationListeners.add(element, functionCallback); +}