blob: 4cab1056ee1198d729f9245108d97aa1706a3a23 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/platform/ax_fragment_root_win.h"
#include "base/no_destructor.h"
#include "ui/accessibility/platform/ax_platform_node_win.h"
#include "ui/base/win/atl_module.h"
namespace ui {
class AXFragmentRootPlatformNodeWin : public AXPlatformNodeWin,
public IRawElementProviderFragmentRoot {
public:
BEGIN_COM_MAP(AXFragmentRootPlatformNodeWin)
COM_INTERFACE_ENTRY(IRawElementProviderFragmentRoot)
COM_INTERFACE_ENTRY_CHAIN(AXPlatformNodeWin)
END_COM_MAP()
static AXFragmentRootPlatformNodeWin* Create(
AXPlatformNodeDelegate* delegate) {
// Make sure ATL is initialized in this module.
win::CreateATLModuleIfNeeded();
CComObject<AXFragmentRootPlatformNodeWin>* instance = nullptr;
HRESULT hr =
CComObject<AXFragmentRootPlatformNodeWin>::CreateInstance(&instance);
DCHECK(SUCCEEDED(hr));
instance->Init(delegate);
instance->AddRef();
return instance;
}
//
// IRawElementProviderSimple methods.
//
STDMETHOD(get_HostRawElementProvider)
(IRawElementProviderSimple** host_element_provider) override {
UIA_VALIDATE_CALL_1_ARG(host_element_provider);
HWND hwnd = GetDelegate()->GetTargetForNativeAccessibilityEvent();
return UiaHostProviderFromHwnd(hwnd, host_element_provider);
}
//
// IRawElementProviderFragment methods.
//
STDMETHOD(get_FragmentRoot)
(IRawElementProviderFragmentRoot** fragment_root) override {
UIA_VALIDATE_CALL_1_ARG(fragment_root);
QueryInterface(IID_PPV_ARGS(fragment_root));
return S_OK;
}
STDMETHOD(GetRuntimeId)(SAFEARRAY** runtime_id) {
UIA_VALIDATE_CALL_1_ARG(runtime_id);
// UIA obtains a runtime ID for a fragment root from the associated HWND.
*runtime_id = nullptr;
return S_OK;
}
//
// IRawElementProviderFragmentRoot methods.
//
// x and y are in pixels, in screen coordinates.
STDMETHOD(ElementProviderFromPoint)
(double x,
double y,
IRawElementProviderFragment** element_provider) override {
UIA_VALIDATE_CALL_1_ARG(element_provider);
*element_provider = nullptr;
gfx::NativeViewAccessible hit_element = nullptr;
// Descend the tree until we get a non-hit or can't go any further.
AXPlatformNode* node_to_test = this;
do {
gfx::NativeViewAccessible test_result =
node_to_test->GetDelegate()->HitTestSync(x, y);
if (test_result != nullptr && test_result != hit_element) {
hit_element = test_result;
node_to_test = AXPlatformNode::FromNativeViewAccessible(test_result);
} else {
node_to_test = nullptr;
}
} while (node_to_test);
if (hit_element)
hit_element->QueryInterface(element_provider);
return S_OK;
}
STDMETHOD(GetFocus)(IRawElementProviderFragment** focus) override {
UIA_VALIDATE_CALL_1_ARG(focus);
*focus = nullptr;
gfx::NativeViewAccessible focused_element = GetDelegate()->GetFocus();
if (focused_element != nullptr) {
focused_element->QueryInterface(IID_PPV_ARGS(focus));
}
return S_OK;
}
};
class AXFragmentRootMapWin {
public:
static AXFragmentRootMapWin& GetInstance() {
static base::NoDestructor<AXFragmentRootMapWin> instance;
return *instance;
}
void AddFragmentRoot(gfx::AcceleratedWidget widget,
AXFragmentRootWin* fragment_root) {
map_[widget] = fragment_root;
}
void RemoveFragmentRoot(gfx::AcceleratedWidget widget) { map_.erase(widget); }
ui::AXFragmentRootWin* GetFragmentRoot(gfx::AcceleratedWidget widget) {
return map_[widget];
}
private:
std::unordered_map<gfx::AcceleratedWidget, AXFragmentRootWin*> map_;
};
AXFragmentRootWin::AXFragmentRootWin(gfx::AcceleratedWidget widget,
gfx::NativeViewAccessible child) {
widget_ = widget;
SetChild(child);
platform_node_ = ui::AXFragmentRootPlatformNodeWin::Create(this);
AXFragmentRootMapWin::GetInstance().AddFragmentRoot(widget, this);
}
AXFragmentRootWin::~AXFragmentRootWin() {
AXFragmentRootMapWin::GetInstance().RemoveFragmentRoot(widget_);
platform_node_->Destroy();
platform_node_ = nullptr;
child_ = nullptr;
}
AXFragmentRootWin* AXFragmentRootWin::GetForAcceleratedWidget(
gfx::AcceleratedWidget widget) {
return AXFragmentRootMapWin::GetInstance().GetFragmentRoot(widget);
}
gfx::NativeViewAccessible AXFragmentRootWin::GetNativeViewAccessible() {
return platform_node_.Get();
}
void AXFragmentRootWin::SetChild(gfx::NativeViewAccessible child) {
if (child != nullptr) {
child_ = static_cast<ui::AXPlatformNodeWin*>(
ui::AXPlatformNode::FromNativeViewAccessible(child));
DCHECK(child_);
} else {
child_ = nullptr;
}
}
int AXFragmentRootWin::GetChildCount() {
return (child_ != nullptr) ? 1 : 0;
}
gfx::NativeViewAccessible AXFragmentRootWin::ChildAtIndex(int index) {
if (index == 0 && child_ != nullptr) {
return child_->GetNativeViewAccessible();
}
return nullptr;
}
gfx::NativeViewAccessible AXFragmentRootWin::HitTestSync(int x, int y) {
if (child_ != nullptr)
return child_->GetDelegate()->HitTestSync(x, y);
return nullptr;
}
gfx::NativeViewAccessible AXFragmentRootWin::GetFocus() {
if (child_ != nullptr)
return child_->GetDelegate()->GetFocus();
return nullptr;
}
const ui::AXUniqueId& AXFragmentRootWin::GetUniqueId() const {
return unique_id_;
}
gfx::AcceleratedWidget
AXFragmentRootWin::GetTargetForNativeAccessibilityEvent() {
return widget_;
}
} // namespace ui