#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) {
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() {
shutdown_loop_.Run();
base::PlatformThread::Join(thread_handle_);
}
void UiaAccessibilityEventWaiter::WaitWithTimeout(base::TimeDelta timeout) {
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() {
base::win::ScopedCOMInitializer com_init{
base::win::ScopedCOMInitializer::kMTA};
CoCreateInstance(CLSID_CUIAutomation, nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&uia_));
CHECK(uia_.Get());
uia_->ElementFromHandle(info_.hwnd, &root_);
CHECK(root_.Get());
ui::win::CreateATLModuleIfNeeded();
CHECK(
SUCCEEDED(CComObject<EventHandler>::CreateInstance(&uia_event_handler_)));
uia_event_handler_->AddRef();
uia_event_handler_->Init(this, root_);
CHECK(SUCCEEDED(uia_->CreateCacheRequest(&cache_request_)));
CHECK(cache_request_.Get());
CHECK(SUCCEEDED(cache_request_->AddProperty(UIA_NamePropertyId)));
CHECK(SUCCEEDED(cache_request_->AddProperty(UIA_AriaRolePropertyId)));
Microsoft::WRL::ComPtr<IUIAutomationCondition> pRawCond;
CHECK(SUCCEEDED(uia_->get_RawViewCondition(&pRawCond)));
CHECK(SUCCEEDED(cache_request_->put_TreeFilter(pRawCond.Get())));
uia_->AddFocusChangedEventHandler(cache_request_.Get(),
uia_event_handler_.Get());
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());
uia_->AddStructureChangedEventHandler(root_.Get(), TreeScope_Subtree,
cache_request_.Get(),
uia_event_handler_.Get());
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());
}
}
uia_->AddAutomationEventHandler(
UIA_LiveRegionChangedEventId, root_.Get(), TreeScope::TreeScope_Subtree,
cache_request_.Get(), uia_event_handler_.Get());
std::move(initialization_complete_).Run();
shutdown_signal_.Wait();
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) {
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) {
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;
}