1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/accessibility/uia_accessibility_event_waiter.h"
#include <algorithm>
#include <numeric>
#include <utility>
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_variant.h"
#include "ui/accessibility/platform/ax_platform_node_win.h"
#include "ui/base/win/atl_module.h"
UiaAccessibilityEventWaiter::UiaAccessibilityEventWaiter(
UiaAccessibilityWaiterInfo info) {
// Create the event thread, and pump messages via |initialization_loop| until
// initialization is complete.
base::RunLoop initialization_loop;
base::PlatformThread::Create(0, &thread_, &thread_handle_);
thread_.Init(this, info, initialization_loop.QuitClosure(),
shutdown_loop_.QuitClosure());
initialization_loop.Run();
}
UiaAccessibilityEventWaiter::~UiaAccessibilityEventWaiter() = default;
void UiaAccessibilityEventWaiter::Wait() {
// Pump messages via |shutdown_loop_| until the thread is complete.
shutdown_loop_.Run();
base::PlatformThread::Join(thread_handle_);
}
void UiaAccessibilityEventWaiter::WaitWithTimeout(base::TimeDelta timeout) {
// Pump messages via |shutdown_loop_| until the thread is complete.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, shutdown_loop_.QuitClosure(), timeout);
shutdown_loop_.Run();
base::PlatformThread::Join(thread_handle_);
}
UiaAccessibilityEventWaiter::Thread::Thread() = default;
UiaAccessibilityEventWaiter::Thread::~Thread() = default;
void UiaAccessibilityEventWaiter::Thread::SendShutdownSignal() {
shutdown_signal_.Signal();
}
void UiaAccessibilityEventWaiter::Thread::Init(
UiaAccessibilityEventWaiter* owner,
const UiaAccessibilityWaiterInfo& info,
base::OnceClosure initialization,
base::OnceClosure shutdown) {
owner_ = owner;
info_ = info;
initialization_complete_ = std::move(initialization);
shutdown_complete_ = std::move(shutdown);
}
void UiaAccessibilityEventWaiter::Thread::ThreadMain() {
// UIA calls must be made on an MTA thread to prevent random timeouts.
base::win::ScopedCOMInitializer com_init{
base::win::ScopedCOMInitializer::kMTA};
// Create an instance of the CUIAutomation class.
CoCreateInstance(CLSID_CUIAutomation, nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&uia_));
CHECK(uia_.Get());
// Find the IUIAutomationElement for the root content window.
uia_->ElementFromHandle(info_.hwnd, &root_);
CHECK(root_.Get());
// Create the event handler.
ui::win::CreateATLModuleIfNeeded();
CHECK(
SUCCEEDED(CComObject<EventHandler>::CreateInstance(&uia_event_handler_)));
uia_event_handler_->AddRef();
uia_event_handler_->Init(this, root_);
// Create a cache request to avoid cross-thread issues when logging.
CHECK(SUCCEEDED(uia_->CreateCacheRequest(&cache_request_)));
CHECK(cache_request_.Get());
CHECK(SUCCEEDED(cache_request_->AddProperty(UIA_NamePropertyId)));
CHECK(SUCCEEDED(cache_request_->AddProperty(UIA_AriaRolePropertyId)));
// Match AccEvent by using Raw View
Microsoft::WRL::ComPtr<IUIAutomationCondition> pRawCond;
CHECK(SUCCEEDED(uia_->get_RawViewCondition(&pRawCond)));
CHECK(SUCCEEDED(cache_request_->put_TreeFilter(pRawCond.Get())));
// Subscribe to focus events.
uia_->AddFocusChangedEventHandler(cache_request_.Get(),
uia_event_handler_.Get());
// Subscribe to all property-change events.
constexpr PROPERTYID kMinProp = UIA_RuntimeIdPropertyId;
constexpr PROPERTYID kMaxProp = UIA_HeadingLevelPropertyId;
std::array<PROPERTYID, (kMaxProp - kMinProp) + 1> property_list;
std::iota(property_list.begin(), property_list.end(), kMinProp);
uia_->AddPropertyChangedEventHandlerNativeArray(
root_.Get(), TreeScope::TreeScope_Subtree, cache_request_.Get(),
uia_event_handler_.Get(), &property_list[0], property_list.size());
// Subscribe to all structure-change events.
uia_->AddStructureChangedEventHandler(root_.Get(), TreeScope_Subtree,
cache_request_.Get(),
uia_event_handler_.Get());
// Subscribe to all automation events (except structure-change events and
// live-region events, which are handled elsewhere).
constexpr EVENTID kMinEvent = UIA_ToolTipOpenedEventId;
constexpr EVENTID kMaxEvent = UIA_NotificationEventId;
for (EVENTID event_id = kMinEvent; event_id <= kMaxEvent; ++event_id) {
if (event_id != UIA_StructureChangedEventId &&
event_id != UIA_LiveRegionChangedEventId) {
uia_->AddAutomationEventHandler(
event_id, root_.Get(), TreeScope::TreeScope_Subtree,
cache_request_.Get(), uia_event_handler_.Get());
}
}
// Subscribe to live-region change events. This must be the last event we
// subscribe to, because |AXFragmentRootWin| will fire events when advised of
// the subscription, and this can hang the test-process (on Windows 19H1+) if
// we're simultaneously trying to subscribe to other events.
uia_->AddAutomationEventHandler(
UIA_LiveRegionChangedEventId, root_.Get(), TreeScope::TreeScope_Subtree,
cache_request_.Get(), uia_event_handler_.Get());
// Signal that initialization is complete; this will wake the main thread to
// start executing the test code after this waiter has been constructed.
std::move(initialization_complete_).Run();
// Wait for shutdown signal.
shutdown_signal_.Wait();
// Cleanup
uia_->RemoveAllEventHandlers();
uia_event_handler_->CleanUp();
uia_event_handler_.Reset();
cache_request_.Reset();
root_.Reset();
uia_.Reset();
std::move(shutdown_complete_).Run();
}
UiaAccessibilityEventWaiter::Thread::EventHandler::EventHandler() = default;
UiaAccessibilityEventWaiter::Thread::EventHandler::~EventHandler() = default;
void UiaAccessibilityEventWaiter::Thread::EventHandler::Init(
UiaAccessibilityEventWaiter::Thread* owner,
Microsoft::WRL::ComPtr<IUIAutomationElement> root) {
owner_ = owner;
root_ = root;
}
void UiaAccessibilityEventWaiter::Thread::EventHandler::CleanUp() {
owner_ = nullptr;
root_.Reset();
}
HRESULT
UiaAccessibilityEventWaiter::Thread::EventHandler::HandleFocusChangedEvent(
IUIAutomationElement* sender) {
// Add focus changed event handling code here.
return S_OK;
}
HRESULT
UiaAccessibilityEventWaiter::Thread::EventHandler::HandlePropertyChangedEvent(
IUIAutomationElement* sender,
PROPERTYID property_id,
VARIANT new_value) {
if (owner_ &&
property_id ==
ui::AXPlatformNodeWin::MojoEventToUIAProperty(owner_->info_.event) &&
MatchesNameRole(sender)) {
owner_->SendShutdownSignal();
}
return S_OK;
}
HRESULT
UiaAccessibilityEventWaiter::Thread::EventHandler::HandleStructureChangedEvent(
IUIAutomationElement* sender,
StructureChangeType change_type,
SAFEARRAY* runtime_id) {
// Add structure changed handling code here.
return S_OK;
}
HRESULT
UiaAccessibilityEventWaiter::Thread::EventHandler::HandleAutomationEvent(
IUIAutomationElement* sender,
EVENTID event_id) {
if (owner_ &&
event_id ==
ui::AXPlatformNodeWin::MojoEventToUIAEvent(owner_->info_.event) &&
MatchesNameRole(sender)) {
owner_->SendShutdownSignal();
}
return S_OK;
}
bool UiaAccessibilityEventWaiter::Thread::EventHandler::MatchesNameRole(
IUIAutomationElement* sender) {
base::win::ScopedBstr aria_role;
base::win::ScopedBstr name;
sender->get_CachedAriaRole(aria_role.Receive());
sender->get_CachedName(name.Receive());
if (std::wstring(aria_role.Get(), SysStringLen(aria_role.Get())) ==
owner_->info_.role &&
std::wstring(name.Get(), SysStringLen(name.Get())) ==
owner_->info_.name) {
return true;
}
return false;
}
|