Chromium Code Reviews| Index: content/browser/accessibility/accessibility_event_recorder_win.cc |
| diff --git a/content/browser/accessibility/accessibility_event_recorder_win.cc b/content/browser/accessibility/accessibility_event_recorder_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6b82a32d69b492ecf62f627b9eb7e25f13007689 |
| --- /dev/null |
| +++ b/content/browser/accessibility/accessibility_event_recorder_win.cc |
| @@ -0,0 +1,164 @@ |
| +// Copyright (c) 2014 The Chromium Authors. All rights reserved. |
|
David Tseng
2014/12/16 16:53:05
Ditto.
dmazzoni
2014/12/16 23:26:32
Done.
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/browser/accessibility/accessibility_event_recorder.h" |
| + |
| +#include <oleacc.h> |
| + |
| +#include <string> |
| + |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/strings/string_util.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/win/scoped_bstr.h" |
| +#include "base/win/scoped_comptr.h" |
| +#include "base/win/scoped_variant.h" |
| +#include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h" |
| +#include "content/browser/accessibility/browser_accessibility_manager.h" |
| +#include "content/browser/accessibility/browser_accessibility_win.h" |
| +#include "third_party/iaccessible2/ia2_api_all.h" |
| +#include "ui/base/win/atl_module.h" |
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +std::string RoleVariantToString(const base::win::ScopedVariant& role) { |
| + if (role.type() == VT_I4) { |
| + return base::UTF16ToUTF8(IAccessibleRoleToString(V_I4(&role))); |
| + } else if (role.type() == VT_BSTR) { |
| + return base::UTF16ToUTF8( |
| + base::string16(V_BSTR(&role), SysStringLen(V_BSTR(&role)))); |
| + } |
| + return std::string(); |
| +} |
| + |
| +HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { |
| + base::win::ScopedComPtr<IServiceProvider> service_provider; |
| + HRESULT hr = accessible->QueryInterface(service_provider.Receive()); |
| + return SUCCEEDED(hr) ? |
| + service_provider->QueryService(IID_IAccessible2, accessible2) : hr; |
| +} |
| + |
| +} |
|
David Tseng
2014/12/16 16:53:05
// namespace
dmazzoni
2014/12/16 23:26:32
Done.
|
| + |
| +class AccessibilityEventRecorderWin : public AccessibilityEventRecorder { |
| + public: |
| + explicit AccessibilityEventRecorderWin(BrowserAccessibilityManager* manager); |
| + virtual ~AccessibilityEventRecorderWin(); |
| + |
| + static void CALLBACK WinEventHookThunk( |
|
David Tseng
2014/12/16 16:53:05
nit: document
dmazzoni
2014/12/16 23:26:32
Done.
|
| + HWINEVENTHOOK handle, |
| + DWORD event, |
| + HWND hwnd, |
| + LONG obj_id, |
| + LONG child_id, |
| + DWORD event_thread, |
| + DWORD event_time); |
| + |
| + private: |
| + void OnWinEventHook(HWINEVENTHOOK handle, |
| + DWORD event, |
| + HWND hwnd, |
| + LONG obj_id, |
| + LONG child_id, |
| + DWORD event_thread, |
| + DWORD event_time); |
| + |
| + HWINEVENTHOOK win_event_hook_handle_; |
| + static AccessibilityEventRecorderWin* instance_; |
| +}; |
| + |
| +// static |
| +AccessibilityEventRecorderWin* |
| +AccessibilityEventRecorderWin::instance_ = nullptr; |
| + |
| +// static |
| +AccessibilityEventRecorder* AccessibilityEventRecorder::Create( |
| + BrowserAccessibilityManager* manager) { |
| + return new AccessibilityEventRecorderWin(manager); |
| +} |
| + |
| +// static |
| +void CALLBACK AccessibilityEventRecorderWin::WinEventHookThunk( |
| + HWINEVENTHOOK handle, |
| + DWORD event, |
| + HWND hwnd, |
| + LONG obj_id, |
| + LONG child_id, |
| + DWORD event_thread, |
| + DWORD event_time) { |
| + if (instance_) { |
| + instance_->OnWinEventHook(handle, event, hwnd, obj_id, child_id, |
| + event_thread, event_time); |
| + } |
| +} |
| + |
| +AccessibilityEventRecorderWin::AccessibilityEventRecorderWin( |
| + BrowserAccessibilityManager* manager) |
| + : AccessibilityEventRecorder(manager) { |
| + CHECK(!instance_) << "There can be only one instance of" |
| + << " WinAccessibilityEventMonitor at a time."; |
| + instance_ = this; |
| + win_event_hook_handle_ = SetWinEventHook( |
| + EVENT_MIN, |
| + EVENT_MAX, |
| + GetModuleHandle(NULL), |
| + &AccessibilityEventRecorderWin::WinEventHookThunk, |
| + GetCurrentProcessId(), |
| + 0, // Hook all threads |
| + WINEVENT_INCONTEXT); |
| +} |
| + |
| +AccessibilityEventRecorderWin::~AccessibilityEventRecorderWin() { |
| + UnhookWinEvent(win_event_hook_handle_); |
| + instance_ = NULL; |
| +} |
| + |
| +void AccessibilityEventRecorderWin::OnWinEventHook( |
| + HWINEVENTHOOK handle, |
| + DWORD event, |
| + HWND hwnd, |
| + LONG obj_id, |
| + LONG child_id, |
| + DWORD event_thread, |
| + DWORD event_time) { |
| + base::win::ScopedComPtr<IAccessible> browser_accessible; |
| + HRESULT hr = AccessibleObjectFromWindow( |
| + hwnd, |
| + obj_id, |
| + IID_IAccessible, |
| + reinterpret_cast<void**>(browser_accessible.Receive())); |
| + CHECK(SUCCEEDED(hr)); |
|
David Tseng
2014/12/16 16:53:05
Maybe print out a message on failure with the abov
dmazzoni
2014/12/16 23:26:32
Done.
|
| + |
| + base::win::ScopedVariant childid_variant(child_id); |
| + base::win::ScopedComPtr<IDispatch> dispatch; |
| + hr = browser_accessible->get_accChild(childid_variant, dispatch.Receive()); |
| + CHECK(SUCCEEDED(hr)); |
| + base::win::ScopedComPtr<IAccessible> iaccessible; |
| + dispatch.QueryInterface(iaccessible.Receive()); |
| + |
| + base::win::ScopedVariant childid_self(CHILDID_SELF); |
| + base::win::ScopedVariant role; |
| + iaccessible->get_accRole(childid_self, role.Receive()); |
| + base::win::ScopedBstr name_bstr; |
| + iaccessible->get_accName(childid_self, name_bstr.Receive()); |
| + base::win::ScopedBstr value_bstr; |
| + iaccessible->get_accValue(childid_self, value_bstr.Receive()); |
| + |
| + std::string log = |
| + base::UTF16ToUTF8(AccessibilityEventToString(event)) + |
| + " on role=" + |
| + RoleVariantToString(role); |
| + base::string16 name(name_bstr, SysStringLen(name_bstr)); |
| + if (!name.empty()) |
| + log += " name=\"" + base::UTF16ToUTF8(name) + "\""; |
| + base::string16 value(value_bstr, SysStringLen(value_bstr)); |
| + if (!value.empty()) |
| + log += " value=\"" + base::UTF16ToUTF8(value) + "\""; |
|
David Tseng
2014/12/16 16:53:05
Could you do a string printf for each of these (or
dmazzoni
2014/12/16 23:26:32
Done.
|
| + |
| + event_logs_.push_back(log); |
|
David Tseng
2014/12/16 16:53:05
Can you get these events out of order?
dmazzoni
2014/12/16 23:26:32
No, this is called synchronously.
|
| +} |
| + |
| +} // namespace content |