Merge MSAA alert functionality with UIA (flutter/engine#38745)

* Use AXFragmentRootWin for MSAA functionality.

Some unused code remains to be removed.

Merge MSAA to AXFragmentRootWin

* Removing unused code

* Remove unused files

* Flip macro

* Formatting

* Licenses

* Make reference

* Disable copy constructor/assignment

* Unused import

* Formatting

* Relocate alert logic

* Remove comment and unused mock

* Fix unit test

* Idempotency

* Formatting

* PR feedback

* Doc comments

* Undo string change for now

* Couple fragment root and alert node

* Formatting

* Add comments

* Pointer to reference

* Typo fix
This commit is contained in:
yaakovschectman
2023-01-19 13:28:27 -05:00
committed by GitHub
parent 595db85227
commit 7ed656d596
23 changed files with 284 additions and 856 deletions

View File

@@ -2405,6 +2405,8 @@ ORIGIN: ../../../flutter/shell/platform/android/vsync_waiter_android.cc + ../../
ORIGIN: ../../../flutter/shell/platform/android/vsync_waiter_android.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/alert_platform_node_delegate.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/alert_platform_node_delegate.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc + ../../../flutter/LICENSE
@@ -3065,12 +3067,8 @@ ORIGIN: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_texture_re
ORIGIN: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_value.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/public/flutter_linux/flutter_linux.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/accessibility_alert.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/accessibility_alert.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/accessibility_bridge_windows.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/accessibility_bridge_windows.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/accessibility_root_node.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/accessibility_root_node.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/angle_surface_manager.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/angle_surface_manager.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/client_wrapper/flutter_engine.cc + ../../../flutter/LICENSE
@@ -4894,6 +4892,8 @@ FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.cc
FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.h
FILE: ../../../flutter/shell/platform/common/accessibility_bridge.cc
FILE: ../../../flutter/shell/platform/common/accessibility_bridge.h
FILE: ../../../flutter/shell/platform/common/alert_platform_node_delegate.cc
FILE: ../../../flutter/shell/platform/common/alert_platform_node_delegate.h
FILE: ../../../flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h
FILE: ../../../flutter/shell/platform/common/client_wrapper/byte_buffer_streams.h
FILE: ../../../flutter/shell/platform/common/client_wrapper/core_implementations.cc
@@ -5573,12 +5573,8 @@ FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_texture_regi
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_value.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h
FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/flutter_linux.h
FILE: ../../../flutter/shell/platform/windows/accessibility_alert.cc
FILE: ../../../flutter/shell/platform/windows/accessibility_alert.h
FILE: ../../../flutter/shell/platform/windows/accessibility_bridge_windows.cc
FILE: ../../../flutter/shell/platform/windows/accessibility_bridge_windows.h
FILE: ../../../flutter/shell/platform/windows/accessibility_root_node.cc
FILE: ../../../flutter/shell/platform/windows/accessibility_root_node.h
FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.cc
FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.h
FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_engine.cc

View File

@@ -80,11 +80,13 @@ source_set("common_cpp_switches") {
source_set("common_cpp_accessibility") {
public = [
"accessibility_bridge.h",
"alert_platform_node_delegate.h",
"flutter_platform_node_delegate.h",
]
sources = [
"accessibility_bridge.cc",
"alert_platform_node_delegate.cc",
"flutter_platform_node_delegate.cc",
]

View File

@@ -0,0 +1,41 @@
// Copyright 2013 The Flutter 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 "alert_platform_node_delegate.h"
namespace flutter {
AlertPlatformNodeDelegate::AlertPlatformNodeDelegate(
ui::AXPlatformNodeDelegate& parent_delegate)
: parent_delegate_(parent_delegate) {
data_.role = ax::mojom::Role::kAlert;
data_.id = id_.Get();
}
AlertPlatformNodeDelegate::~AlertPlatformNodeDelegate() {}
gfx::AcceleratedWidget
AlertPlatformNodeDelegate::GetTargetForNativeAccessibilityEvent() {
return parent_delegate_.GetTargetForNativeAccessibilityEvent();
}
gfx::NativeViewAccessible AlertPlatformNodeDelegate::GetParent() {
return parent_delegate_.GetNativeViewAccessible();
}
const ui::AXUniqueId& AlertPlatformNodeDelegate::GetUniqueId() const {
return id_;
}
const ui::AXNodeData& AlertPlatformNodeDelegate::GetData() const {
return data_;
}
void AlertPlatformNodeDelegate::SetText(const std::u16string& text) {
data_.SetName(text);
data_.SetDescription(text);
data_.SetValue(text);
}
} // namespace flutter

View File

@@ -0,0 +1,51 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_COMMON_ALERT_PLATFORM_NODE_DELEGATE_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_ALERT_PLATFORM_NODE_DELEGATE_H_
#include "flutter/third_party/accessibility/ax/ax_node_data.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate_base.h"
namespace flutter {
// A delegate for a node that holds the text of an a11y alert that a
// screen-reader should announce. The delegate is used to construct an
// AXPlatformNode, and in order to serve as an alert, only needs to be able to
// hold a text announcement and make that text available to the platform node.
class AlertPlatformNodeDelegate : public ui::AXPlatformNodeDelegateBase {
public:
explicit AlertPlatformNodeDelegate(
ui::AXPlatformNodeDelegate& parent_delegate);
~AlertPlatformNodeDelegate();
AlertPlatformNodeDelegate(const AlertPlatformNodeDelegate& other) = delete;
AlertPlatformNodeDelegate operator=(const AlertPlatformNodeDelegate& other) =
delete;
// Set the alert text of the node for which this is the delegate.
void SetText(const std::u16string& text);
// |AXPlatformNodeDelegate|
gfx::NativeViewAccessible GetParent() override;
private:
// AXPlatformNodeDelegate overrides.
gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
const ui::AXUniqueId& GetUniqueId() const override;
const ui::AXNodeData& GetData() const override;
// Delegate of the parent of this node. Returned by GetParent.
ui::AXPlatformNodeDelegate& parent_delegate_;
// Node Data that contains the alert text. Returned by GetData.
ui::AXNodeData data_;
// A unique ID used to identify this node. Returned by GetUniqueId.
ui::AXUniqueId id_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_COMMON_ALERT_PLATFORM_NODE_DELEGATE_H_

View File

@@ -38,12 +38,8 @@ source_set("flutter_windows_headers") {
source_set("flutter_windows_source") {
# Common Windows sources.
sources = [
"accessibility_alert.cc",
"accessibility_alert.h",
"accessibility_bridge_windows.cc",
"accessibility_bridge_windows.h",
"accessibility_root_node.cc",
"accessibility_root_node.h",
"angle_surface_manager.cc",
"angle_surface_manager.h",
"cursor_handler.cc",

View File

@@ -1,187 +0,0 @@
// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/accessibility_alert.h"
#include "flutter/shell/platform/windows/accessibility_root_node.h"
namespace flutter {
AccessibilityAlert::AccessibilityAlert() : text_(L""), parent_(nullptr) {}
// IAccessible methods.
IFACEMETHODIMP AccessibilityAlert::accHitTest(LONG screen_physical_pixel_x,
LONG screen_physical_pixel_y,
VARIANT* child) {
child->vt = VT_EMPTY;
return S_FALSE;
}
// Performs the object's default action.
IFACEMETHODIMP AccessibilityAlert::accDoDefaultAction(VARIANT var_id) {
return E_FAIL;
}
// Retrieves an IDispatch interface pointer for the specified child.
IFACEMETHODIMP AccessibilityAlert::get_accChild(VARIANT var_child,
IDispatch** disp_child) {
if (V_VT(&var_child) == VT_I4 && V_I4(&var_child) == CHILDID_SELF) {
*disp_child = this;
AddRef();
return S_OK;
}
*disp_child = nullptr;
return E_FAIL;
}
// Retrieves the number of accessible children.
IFACEMETHODIMP AccessibilityAlert::get_accChildCount(LONG* child_count) {
*child_count = 0;
return S_OK;
}
// Retrieves the tooltip description.
IFACEMETHODIMP AccessibilityAlert::get_accDescription(VARIANT var_id,
BSTR* desc) {
*desc = SysAllocString(text_.c_str());
return S_OK;
}
// Retrieves the name of the specified object.
IFACEMETHODIMP AccessibilityAlert::get_accName(VARIANT var_id, BSTR* name) {
*name = SysAllocString(text_.c_str());
return S_OK;
}
// Retrieves the IDispatch interface of the object's parent.
IFACEMETHODIMP AccessibilityAlert::get_accParent(IDispatch** disp_parent) {
*disp_parent = parent_;
if (*disp_parent) {
(*disp_parent)->AddRef();
return S_OK;
}
return S_FALSE;
}
// Retrieves information describing the role of the specified object.
IFACEMETHODIMP AccessibilityAlert::get_accRole(VARIANT var_id, VARIANT* role) {
*role = {.vt = VT_I4, .lVal = ROLE_SYSTEM_ALERT};
return S_OK;
}
// Retrieves the current state of the specified object.
IFACEMETHODIMP AccessibilityAlert::get_accState(VARIANT var_id,
VARIANT* state) {
*state = {.vt = VT_I4, .lVal = STATE_SYSTEM_DEFAULT};
return S_OK;
}
// Gets the help string for the specified object.
IFACEMETHODIMP AccessibilityAlert::get_accHelp(VARIANT var_id, BSTR* help) {
*help = SysAllocString(L"");
return S_OK;
}
// Retrieve or set the string value associated with the specified object.
// Setting the value is not typically used by screen readers, but it's
// used frequently by automation software.
IFACEMETHODIMP AccessibilityAlert::get_accValue(VARIANT var_id, BSTR* value) {
*value = SysAllocString(text_.c_str());
return S_OK;
}
// IAccessible methods not implemented.
IFACEMETHODIMP AccessibilityAlert::get_accSelection(VARIANT* selected) {
selected->vt = VT_EMPTY;
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::accSelect(LONG flags_sel, VARIANT var_id) {
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::put_accValue(VARIANT var_id,
BSTR new_value) {
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::get_accFocus(VARIANT* focus_child) {
focus_child->vt = VT_EMPTY;
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::get_accHelpTopic(BSTR* help_file,
VARIANT var_id,
LONG* topic_id) {
if (help_file) {
*help_file = nullptr;
}
if (topic_id) {
*topic_id = 0;
}
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::put_accName(VARIANT var_id, BSTR put_name) {
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::get_accKeyboardShortcut(VARIANT var_id,
BSTR* access_key) {
*access_key = nullptr;
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::accLocation(LONG* physical_pixel_left,
LONG* physical_pixel_top,
LONG* width,
LONG* height,
VARIANT var_id) {
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::accNavigate(LONG nav_dir,
VARIANT start,
VARIANT* end) {
end->vt = VT_EMPTY;
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityAlert::get_accDefaultAction(VARIANT var_id,
BSTR* default_action) {
*default_action = nullptr;
return E_NOTIMPL;
}
// End of IAccessible methods.
//
// IServiceProvider implementation.
//
IFACEMETHODIMP AccessibilityAlert::QueryService(REFGUID guidService,
REFIID riid,
void** object) {
if (!object) {
return E_INVALIDARG;
}
if (guidService == IID_IAccessible) {
return QueryInterface(riid, object);
}
*object = nullptr;
return E_FAIL;
}
void AccessibilityAlert::SetText(const std::wstring& text) {
text_ = text;
}
void AccessibilityAlert::SetParent(AccessibilityRootNode* parent) {
parent_ = parent;
}
} // namespace flutter

View File

@@ -1,123 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_ALERT_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_ALERT_H_
#include <atlbase.h>
#include <atlcom.h>
#include <oleacc.h>
#include <string>
namespace flutter {
class AccessibilityRootNode;
// An IAccessible node representing an alert read to the screen reader.
// When an announcement is requested by the framework, an instance of
// this class, if none exists already, is created and made a child of
// the root AccessibilityRootNode node, and is therefore also a sibling
// of the window's root node.
// This node is not interactable to the user.
class __declspec(uuid("778c1bd8-383f-4d49-b6be-8937e12b6a32"))
AccessibilityAlert : public CComObjectRootEx<CComMultiThreadModel>,
public IDispatchImpl<IAccessible>,
public IServiceProvider {
public:
BEGIN_COM_MAP(AccessibilityAlert)
COM_INTERFACE_ENTRY(AccessibilityAlert)
COM_INTERFACE_ENTRY(IAccessible)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IServiceProvider)
END_COM_MAP()
//
// IAccessible methods.
//
// Retrieves the child element or child object at a given point on the screen.
IFACEMETHODIMP accHitTest(LONG screen_physical_pixel_x,
LONG screen_physical_pixel_y,
VARIANT* child) override;
// Retrieves an IDispatch interface pointer for the specified child.
IFACEMETHODIMP get_accChild(VARIANT var_child,
IDispatch** disp_child) override;
// Retrieves the number of accessible children.
IFACEMETHODIMP get_accChildCount(LONG* child_count) override;
// Retrieves a string that describes the object's default action.
IFACEMETHODIMP get_accDefaultAction(VARIANT var_id,
BSTR* default_action) override;
// Retrieves the tooltip description.
IFACEMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override;
// Retrieves the name of the specified object.
IFACEMETHODIMP get_accName(VARIANT var_id, BSTR* name) override;
// Retrieves the IDispatch interface of the object's parent.
IFACEMETHODIMP get_accParent(IDispatch** disp_parent) override;
// Retrieves information describing the role of the specified object.
IFACEMETHODIMP get_accRole(VARIANT var_id, VARIANT* role) override;
// Retrieves the current state of the specified object.
IFACEMETHODIMP get_accState(VARIANT var_id, VARIANT* state) override;
// Gets the help string for the specified object.
IFACEMETHODIMP get_accHelp(VARIANT var_id, BSTR* help) override;
// Retrieve the string value associated with the specified object.
IFACEMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override;
// IAccessible methods not implemented.
IFACEMETHODIMP accLocation(LONG* physical_pixel_left,
LONG* physical_pixel_top,
LONG* width,
LONG* height,
VARIANT var_id) override;
IFACEMETHODIMP accNavigate(LONG nav_dir,
VARIANT start,
VARIANT* end) override;
IFACEMETHODIMP accDoDefaultAction(VARIANT var_id) override;
IFACEMETHODIMP get_accFocus(VARIANT* focus_child) override;
IFACEMETHODIMP get_accKeyboardShortcut(VARIANT var_id,
BSTR* access_key) override;
IFACEMETHODIMP get_accSelection(VARIANT* selected) override;
IFACEMETHODIMP accSelect(LONG flags_sel, VARIANT var_id) override;
IFACEMETHODIMP get_accHelpTopic(BSTR* help_file,
VARIANT var_id,
LONG* topic_id) override;
IFACEMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override;
IFACEMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override;
// End of IAccessible methods.
//
// IServiceProvider method.
//
IFACEMETHODIMP QueryService(REFGUID guidService,
REFIID riid,
void** object) override;
AccessibilityAlert();
~AccessibilityAlert() = default;
// Sets the text of this alert to the provided message.
void SetText(const std::wstring& text);
void SetParent(AccessibilityRootNode* parent);
private:
std::wstring text_;
AccessibilityRootNode* parent_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_ALERT_H_

View File

@@ -1,306 +0,0 @@
// Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/accessibility_root_node.h"
#include "flutter/fml/logging.h"
#include "flutter/third_party/accessibility/base/win/atl_module.h"
namespace flutter {
static constexpr LONG kWindowChildId = 1;
static constexpr LONG kInvalidChildId = 3;
AccessibilityRootNode::AccessibilityRootNode() : alert_accessible_(nullptr) {}
AccessibilityRootNode::~AccessibilityRootNode() {
if (alert_accessible_) {
alert_accessible_->Release();
alert_accessible_ = nullptr;
}
}
IAccessible* AccessibilityRootNode::GetTargetAndChildID(VARIANT* var_id) {
LONG& child_id = var_id->lVal;
if (V_VT(var_id) != VT_I4) {
child_id = kInvalidChildId;
return nullptr;
}
child_id = V_I4(var_id);
if (!window_accessible_) {
return nullptr;
}
if (child_id == CHILDID_SELF || child_id == kWindowChildId) {
child_id = CHILDID_SELF;
return window_accessible_;
}
if (child_id == kAlertChildId && alert_accessible_) {
child_id = CHILDID_SELF;
return alert_accessible_;
}
// A negative child ID can be used to refer to an AX node directly by its ID.
if (child_id < 0) {
return window_accessible_;
}
return nullptr;
}
IFACEMETHODIMP AccessibilityRootNode::accHitTest(LONG screen_physical_pixel_x,
LONG screen_physical_pixel_y,
VARIANT* child) {
if (window_accessible_) {
return window_accessible_->accHitTest(screen_physical_pixel_x,
screen_physical_pixel_y, child);
}
child->vt = VT_EMPTY;
return S_FALSE;
}
IFACEMETHODIMP AccessibilityRootNode::accDoDefaultAction(VARIANT var_id) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->accDoDefaultAction(var_id);
}
return E_FAIL;
}
IFACEMETHODIMP AccessibilityRootNode::accLocation(LONG* physical_pixel_left,
LONG* physical_pixel_top,
LONG* width,
LONG* height,
VARIANT var_id) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->accLocation(physical_pixel_left, physical_pixel_top, width,
height, var_id);
}
return S_FALSE;
}
IFACEMETHODIMP AccessibilityRootNode::accNavigate(LONG nav_dir,
VARIANT start,
VARIANT* end) {
IAccessible* target;
if ((target = GetTargetAndChildID(&start))) {
return target->accNavigate(nav_dir, start, end);
}
return S_FALSE;
}
IFACEMETHODIMP AccessibilityRootNode::get_accChild(VARIANT var_child,
IDispatch** disp_child) {
if (V_VT(&var_child) != VT_I4) {
return E_FAIL;
}
LONG child_id = V_I4(&var_child);
if (child_id == CHILDID_SELF) {
*disp_child = this;
} else if (!window_accessible_) {
return E_FAIL;
} else if (child_id == kWindowChildId) {
*disp_child = window_accessible_;
} else if (child_id == kAlertChildId && alert_accessible_) {
*disp_child = alert_accessible_;
} else if (child_id < 0) {
// A negative child ID can be used to refer to an AX node directly by its
// ID.
return window_accessible_->get_accChild(var_child, disp_child);
} else {
return E_FAIL;
}
(*disp_child)->AddRef();
return S_OK;
}
IFACEMETHODIMP AccessibilityRootNode::get_accChildCount(LONG* child_count) {
LONG children = 0;
if (window_accessible_) {
children++;
}
if (alert_accessible_) {
children++;
}
*child_count = children;
return S_OK;
}
IFACEMETHODIMP AccessibilityRootNode::get_accDefaultAction(VARIANT var_id,
BSTR* def_action) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->get_accDefaultAction(var_id, def_action);
}
*def_action = nullptr;
return S_FALSE;
}
IFACEMETHODIMP AccessibilityRootNode::get_accDescription(VARIANT var_id,
BSTR* desc) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->get_accDescription(var_id, desc);
}
return E_FAIL;
}
IFACEMETHODIMP AccessibilityRootNode::get_accFocus(VARIANT* focus_child) {
if (window_accessible_) {
return window_accessible_->get_accFocus(focus_child);
}
focus_child->vt = VT_EMPTY;
return S_OK;
}
IFACEMETHODIMP AccessibilityRootNode::get_accKeyboardShortcut(VARIANT var_id,
BSTR* acc_key) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->get_accKeyboardShortcut(var_id, acc_key);
}
return E_FAIL;
}
IFACEMETHODIMP AccessibilityRootNode::get_accName(VARIANT var_id,
BSTR* name_bstr) {
if (V_I4(&var_id) == CHILDID_SELF) {
std::wstring name = L"ROOT_NODE_VIEW";
*name_bstr = SysAllocString(name.c_str());
return S_OK;
}
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->get_accName(var_id, name_bstr);
}
return S_FALSE;
}
IFACEMETHODIMP AccessibilityRootNode::get_accParent(IDispatch** disp_parent) {
return S_FALSE;
}
IFACEMETHODIMP AccessibilityRootNode::get_accRole(VARIANT var_id,
VARIANT* role) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->get_accRole(var_id, role);
}
return E_FAIL;
}
IFACEMETHODIMP AccessibilityRootNode::get_accState(VARIANT var_id,
VARIANT* state) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->get_accState(var_id, state);
}
return E_FAIL;
}
IFACEMETHODIMP AccessibilityRootNode::get_accHelp(VARIANT var_id, BSTR* help) {
if (!help) {
return E_INVALIDARG;
}
*help = {};
return S_FALSE;
}
IFACEMETHODIMP AccessibilityRootNode::get_accValue(VARIANT var_id,
BSTR* value) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->get_accValue(var_id, value);
}
return E_FAIL;
}
IFACEMETHODIMP AccessibilityRootNode::put_accValue(VARIANT var_id,
BSTR new_value) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->put_accValue(var_id, new_value);
}
return E_FAIL;
}
IFACEMETHODIMP AccessibilityRootNode::get_accSelection(VARIANT* selected) {
selected->vt = VT_EMPTY;
return S_OK;
}
IFACEMETHODIMP AccessibilityRootNode::accSelect(LONG flagsSelect,
VARIANT var_id) {
IAccessible* target;
if ((target = GetTargetAndChildID(&var_id))) {
return target->accSelect(flagsSelect, var_id);
}
return E_FAIL;
}
IFACEMETHODIMP AccessibilityRootNode::get_accHelpTopic(BSTR* help_file,
VARIANT var_id,
LONG* topic_id) {
if (help_file) {
*help_file = nullptr;
}
if (topic_id) {
*topic_id = -1;
}
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibilityRootNode::put_accName(VARIANT var_id,
BSTR put_name) {
return E_NOTIMPL;
}
//
// IServiceProvider implementation.
//
IFACEMETHODIMP AccessibilityRootNode::QueryService(REFGUID guidService,
REFIID riid,
void** object) {
if (!object) {
return E_INVALIDARG;
}
if (guidService == IID_IAccessible) {
return QueryInterface(riid, object);
}
*object = nullptr;
return E_FAIL;
}
void AccessibilityRootNode::SetWindow(IAccessible* window) {
window_accessible_ = window;
}
AccessibilityAlert* AccessibilityRootNode::GetOrCreateAlert() {
if (!alert_accessible_) {
CComObject<AccessibilityAlert>* instance = nullptr;
HRESULT hr = CComObject<AccessibilityAlert>::CreateInstance(&instance);
if (!SUCCEEDED(hr)) {
FML_LOG(FATAL) << "Failed to create alert accessible";
}
instance->AddRef();
instance->SetParent(this);
alert_accessible_ = instance;
}
return alert_accessible_;
}
// static
AccessibilityRootNode* AccessibilityRootNode::Create() {
ui::win::CreateATLModuleIfNeeded();
CComObject<AccessibilityRootNode>* instance = nullptr;
HRESULT hr = CComObject<AccessibilityRootNode>::CreateInstance(&instance);
if (!SUCCEEDED(hr) || !instance) {
FML_LOG(FATAL) << "Failed to create accessibility root node";
}
instance->AddRef();
return instance;
}
} // namespace flutter

View File

@@ -1,137 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_ROOT_NODE_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_ROOT_NODE_H_
#include <atlbase.h>
#include <atlcom.h>
#include <oleacc.h>
#include <memory>
#include "flutter/shell/platform/windows/accessibility_alert.h"
namespace flutter {
// A parent node that wraps the window IAccessible node.
class __declspec(uuid("fedb8280-ea4f-47a9-98fe-5d1a557fe4b3"))
AccessibilityRootNode : public CComObjectRootEx<CComMultiThreadModel>,
public IDispatchImpl<IAccessible>,
public IServiceProvider {
public:
static constexpr LONG kAlertChildId = 2;
BEGIN_COM_MAP(AccessibilityRootNode)
COM_INTERFACE_ENTRY(AccessibilityRootNode)
COM_INTERFACE_ENTRY(IAccessible)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IServiceProvider)
END_COM_MAP()
//
// IAccessible methods.
//
// Retrieves the child element or child object at a given point on the screen.
IFACEMETHODIMP accHitTest(LONG screen_physical_pixel_x,
LONG screen_physical_pixel_y,
VARIANT* child) override;
// Performs the object's default action.
IFACEMETHODIMP accDoDefaultAction(VARIANT var_id) override;
// Retrieves the specified object's current screen location.
IFACEMETHODIMP accLocation(LONG* physical_pixel_left,
LONG* physical_pixel_top,
LONG* width,
LONG* height,
VARIANT var_id) override;
// Traverses to another UI element and retrieves the object.
IFACEMETHODIMP accNavigate(LONG nav_dir,
VARIANT start,
VARIANT* end) override;
// Retrieves an IDispatch interface pointer for the specified child.
IFACEMETHODIMP get_accChild(VARIANT var_child,
IDispatch** disp_child) override;
// Retrieves the number of accessible children.
IFACEMETHODIMP get_accChildCount(LONG* child_count) override;
// Retrieves a string that describes the object's default action.
IFACEMETHODIMP get_accDefaultAction(VARIANT var_id,
BSTR* default_action) override;
// Retrieves the tooltip description.
IFACEMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override;
// Retrieves the object that has the keyboard focus.
IFACEMETHODIMP get_accFocus(VARIANT* focus_child) override;
// Retrieves the specified object's shortcut.
IFACEMETHODIMP get_accKeyboardShortcut(VARIANT var_id,
BSTR* access_key) override;
// Retrieves the name of the specified object.
IFACEMETHODIMP get_accName(VARIANT var_id, BSTR* name) override;
// Retrieves the IDispatch interface of the object's parent.
IFACEMETHODIMP get_accParent(IDispatch** disp_parent) override;
// Retrieves information describing the role of the specified object.
IFACEMETHODIMP get_accRole(VARIANT var_id, VARIANT* role) override;
// Retrieves the current state of the specified object.
IFACEMETHODIMP get_accState(VARIANT var_id, VARIANT* state) override;
// Gets the help string for the specified object.
IFACEMETHODIMP get_accHelp(VARIANT var_id, BSTR* help) override;
// Retrieve or set the string value associated with the specified object.
// Setting the value is not typically used by screen readers, but it's
// used frequently by automation software.
IFACEMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override;
IFACEMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override;
// IAccessible methods not implemented.
IFACEMETHODIMP get_accSelection(VARIANT* selected) override;
IFACEMETHODIMP accSelect(LONG flags_sel, VARIANT var_id) override;
IFACEMETHODIMP get_accHelpTopic(BSTR* help_file,
VARIANT var_id,
LONG* topic_id) override;
IFACEMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override;
//
// IServiceProvider method.
//
IFACEMETHODIMP QueryService(REFGUID guidService,
REFIID riid,
void** object) override;
AccessibilityRootNode();
virtual ~AccessibilityRootNode();
void SetWindow(IAccessible* window);
void SetAlert(AccessibilityAlert* alert);
AccessibilityAlert* GetOrCreateAlert();
static AccessibilityRootNode* Create();
private:
// Helper method to redirect method calls to the contained window or alert.
IAccessible* GetTargetAndChildID(VARIANT* var_id);
IAccessible* window_accessible_;
AccessibilityAlert* alert_accessible_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_ROOT_NODE_H_

View File

@@ -298,15 +298,18 @@ void FlutterWindow::SendInitialAccessibilityFeatures() {
OnThemeChange();
}
AccessibilityRootNode* FlutterWindow::GetAccessibilityRootNode() {
if (!accessibility_root_) {
CreateAccessibilityRootNode();
}
return accessibility_root_;
}
ui::AXFragmentRootDelegateWin* FlutterWindow::GetAxFragmentRootDelegate() {
return binding_handler_delegate_->GetAxFragmentRootDelegate();
}
AlertPlatformNodeDelegate* FlutterWindow::GetAlertDelegate() {
CreateAxFragmentRoot();
return alert_delegate_.get();
}
ui::AXPlatformNodeWin* FlutterWindow::GetAlert() {
CreateAxFragmentRoot();
return alert_node_.get();
}
} // namespace flutter

View File

@@ -150,7 +150,10 @@ class FlutterWindow : public Window, public WindowBindingHandler {
void SendInitialAccessibilityFeatures() override;
// |WindowBindingHandler|
AccessibilityRootNode* GetAccessibilityRootNode() override;
AlertPlatformNodeDelegate* GetAlertDelegate() override;
// |WindowBindingHandler|
ui::AXPlatformNodeWin* GetAlert() override;
// |Window|
ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() override;

View File

@@ -142,6 +142,7 @@ class MockFlutterWindow : public FlutterWindow {
MOCK_METHOD4(Win32PeekMessage, BOOL(LPMSG, UINT, UINT, UINT));
MOCK_METHOD1(Win32MapVkToChar, uint32_t(uint32_t));
MOCK_METHOD0(GetPlatformWindow, HWND());
MOCK_METHOD0(GetAxFragmentRootDelegate, ui::AXFragmentRootDelegateWin*());
protected:
// |KeyboardManager::WindowDelegate|
@@ -164,7 +165,8 @@ class TestFlutterWindowsView : public FlutterWindowsView {
SpyKeyboardKeyHandler* key_event_handler;
SpyTextInputPlugin* text_input_plugin;
MOCK_METHOD4(NotifyWinEventWrapper, void(DWORD, HWND, LONG, LONG));
MOCK_METHOD2(NotifyWinEventWrapper,
void(ui::AXPlatformNodeWin*, ax::mojom::Event));
protected:
std::unique_ptr<KeyboardHandlerBase> CreateKeyboardKeyHandler(
@@ -415,15 +417,15 @@ TEST(FlutterWindowTest, AlertNode) {
std::unique_ptr<MockFlutterWindow> win32window =
std::make_unique<MockFlutterWindow>();
ON_CALL(*win32window, GetPlatformWindow()).WillByDefault(Return(nullptr));
AccessibilityRootNode* root_node = win32window->GetAccessibilityRootNode();
ON_CALL(*win32window, GetAxFragmentRootDelegate())
.WillByDefault(Return(nullptr));
TestFlutterWindowsView view(std::move(win32window));
EXPECT_CALL(view,
NotifyWinEventWrapper(EVENT_SYSTEM_ALERT, nullptr, OBJID_CLIENT,
AccessibilityRootNode::kAlertChildId))
.Times(1);
std::wstring message = L"Test alert";
EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
.Times(1);
view.AnnounceAlert(message);
IAccessible* alert = root_node->GetOrCreateAlert();
IAccessible* alert = view.AlertNode();
VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
BSTR strptr;
alert->get_accName(self, &strptr);

View File

@@ -12,6 +12,7 @@
#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
#include "flutter/shell/platform/windows/testing/test_keyboard.h"
#include "flutter/shell/platform/windows/testing/windows_test.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
#include "fml/synchronization/waitable_event.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -567,7 +568,8 @@ class MockFlutterWindowsView : public FlutterWindowsView {
: FlutterWindowsView(std::move(wbh)) {}
~MockFlutterWindowsView() {}
MOCK_METHOD4(NotifyWinEventWrapper, void(DWORD, HWND, LONG, LONG));
MOCK_METHOD2(NotifyWinEventWrapper,
void(ui::AXPlatformNodeWin*, ax::mojom::Event));
};
TEST_F(FlutterWindowsEngineTest, AlertPlatformMessage) {
@@ -576,9 +578,11 @@ TEST_F(FlutterWindowsEngineTest, AlertPlatformMessage) {
auto window_binding_handler =
std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
AccessibilityRootNode* root_node = AccessibilityRootNode::Create();
ON_CALL(*window_binding_handler, GetAccessibilityRootNode)
.WillByDefault(::testing::Return(root_node));
ui::AXPlatformNodeDelegateBase parent_delegate;
AlertPlatformNodeDelegate delegate(parent_delegate);
ON_CALL(*window_binding_handler, GetAlertDelegate).WillByDefault([&delegate] {
return &delegate;
});
MockFlutterWindowsView view(std::move(window_binding_handler));
view.SetEngine(builder.Build());
FlutterWindowsEngine* engine = view.GetEngine();
@@ -598,9 +602,8 @@ TEST_F(FlutterWindowsEngineTest, AlertPlatformMessage) {
bool did_call = false;
ON_CALL(view, NotifyWinEventWrapper)
.WillByDefault([&did_call](DWORD event, HWND hwnd, LONG obj, LONG child) {
did_call = true;
});
.WillByDefault([&did_call](ui::AXPlatformNodeWin* node,
ax::mojom::Event event) { did_call = true; });
engine->UpdateSemanticsEnabled(true);
engine->Run();

View File

@@ -10,6 +10,7 @@
#include "flutter/shell/platform/windows/keyboard_key_channel_handler.h"
#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h"
#include "flutter/shell/platform/windows/text_input_plugin.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
namespace flutter {
@@ -661,23 +662,19 @@ FlutterWindowsEngine* FlutterWindowsView::GetEngine() {
}
void FlutterWindowsView::AnnounceAlert(const std::wstring& text) {
AccessibilityRootNode* root_node =
binding_handler_->GetAccessibilityRootNode();
AccessibilityAlert* alert =
binding_handler_->GetAccessibilityRootNode()->GetOrCreateAlert();
alert->SetText(text);
HWND hwnd = GetPlatformWindow();
NotifyWinEventWrapper(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT,
AccessibilityRootNode::kAlertChildId);
auto alert_delegate = binding_handler_->GetAlertDelegate();
if (!alert_delegate) {
return;
}
alert_delegate->SetText(base::WideToUTF16(text));
ui::AXPlatformNodeWin* alert_node = binding_handler_->GetAlert();
NotifyWinEventWrapper(alert_node, ax::mojom::Event::kAlert);
}
void FlutterWindowsView::NotifyWinEventWrapper(DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild) {
if (hwnd) {
NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT,
AccessibilityRootNode::kAlertChildId);
void FlutterWindowsView::NotifyWinEventWrapper(ui::AXPlatformNodeWin* node,
ax::mojom::Event event) {
if (node) {
node->NotifyAccessibilityEvent(event);
}
}
@@ -685,4 +682,8 @@ ui::AXFragmentRootDelegateWin* FlutterWindowsView::GetAxFragmentRootDelegate() {
return engine_->accessibility_bridge().lock().get();
}
ui::AXPlatformNodeWin* FlutterWindowsView::AlertNode() const {
return binding_handler_->GetAlert();
}
} // namespace flutter

View File

@@ -202,6 +202,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
// |TextInputPluginDelegate|
void OnResetImeComposing() override;
// Get a pointer to the alert node for this view.
ui::AXPlatformNodeWin* AlertNode() const;
// |WindowBindingHandlerDelegate|
virtual ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() override;
@@ -221,10 +224,8 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
virtual std::unique_ptr<TextInputPlugin> CreateTextInputPlugin(
BinaryMessenger* messenger);
virtual void NotifyWinEventWrapper(DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild);
virtual void NotifyWinEventWrapper(ui::AXPlatformNodeWin* node,
ax::mojom::Event event);
private:
// Struct holding the state of an individual pointer. The engine doesn't keep

View File

@@ -6,6 +6,7 @@
#define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_WINDOW_BINDING_HANDLER_H_
#include "flutter/shell/platform/windows/window_binding_handler.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
#include "gmock/gmock.h"
namespace flutter {
@@ -36,7 +37,8 @@ class MockWindowBindingHandler : public WindowBindingHandler {
bool(const void* allocation, size_t row_bytes, size_t height));
MOCK_METHOD0(GetPrimaryPointerLocation, PointerLocation());
MOCK_METHOD0(SendInitialAccessibilityFeatures, void());
MOCK_METHOD0(GetAccessibilityRootNode, AccessibilityRootNode*());
MOCK_METHOD0(GetAlertDelegate, AlertPlatformNodeDelegate*());
MOCK_METHOD0(GetAlert, ui::AXPlatformNodeWin*());
};
} // namespace testing

View File

@@ -14,6 +14,7 @@
#include <cstring>
#include "flutter/shell/platform/common/flutter_platform_node_delegate.h"
#include "flutter/shell/platform/windows/dpi_utils.h"
#include "flutter/shell/platform/windows/keyboard_utils.h"
@@ -60,7 +61,6 @@ Window::Window(std::unique_ptr<WindowsProcTable> windows_proc_table,
: touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId),
windows_proc_table_(std::move(windows_proc_table)),
text_input_manager_(std::move(text_input_manager)),
accessibility_root_(nullptr),
ax_fragment_root_(nullptr) {
// Get the DPI of the primary monitor as the initial DPI. If Per-Monitor V2 is
// supported, |current_dpi_| should be updated in the
@@ -204,35 +204,31 @@ LRESULT Window::OnGetObject(UINT const message,
gfx::NativeViewAccessible root_view = GetNativeViewAccessible();
// TODO(schectman): UIA is currently disabled by default.
// https://github.com/flutter/flutter/issues/114547
if (is_uia_request && root_view) {
if (root_view) {
CreateAxFragmentRoot();
if (is_uia_request) {
#ifdef FLUTTER_ENGINE_USE_UIA
if (!ax_fragment_root_) {
ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
window_handle_, GetAxFragmentRootDelegate());
}
// Retrieve UIA object for the root view.
Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
if (SUCCEEDED(ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
IID_PPV_ARGS(&root)))) {
// Return the UIA object via UiaReturnRawElementProvider(). See:
// https://docs.microsoft.com/en-us/windows/win32/winauto/wm-getobject
reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
lparam, root.Get());
} else {
FML_LOG(ERROR) << "Failed to query AX fragment root.";
}
// Retrieve UIA object for the root view.
Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
if (SUCCEEDED(
ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
IID_PPV_ARGS(&root)))) {
// Return the UIA object via UiaReturnRawElementProvider(). See:
// https://docs.microsoft.com/en-us/windows/win32/winauto/wm-getobject
reference_result = UiaReturnRawElementProvider(window_handle_, wparam,
lparam, root.Get());
} else {
FML_LOG(ERROR) << "Failed to query AX fragment root.";
}
#endif // FLUTTER_ENGINE_USE_UIA
} else if (is_msaa_request && root_view) {
// Create the accessibility root if it does not already exist.
if (!accessibility_root_) {
CreateAccessibilityRootNode();
} else if (is_msaa_request) {
// Create the accessibility root if it does not already exist.
// Return the IAccessible for the root view.
Microsoft::WRL::ComPtr<IAccessible> root;
ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
IID_PPV_ARGS(&root));
reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
}
// Return the IAccessible for the root view.
// Microsoft::WRL::ComPtr<IAccessible> root(root_view);
accessibility_root_->SetWindow(root_view);
Microsoft::WRL::ComPtr<IAccessible> root(accessibility_root_);
reference_result = LresultFromObject(IID_IAccessible, wparam, root.Get());
}
return reference_result;
}
@@ -621,11 +617,6 @@ void Window::Destroy() {
window_handle_ = nullptr;
}
if (accessibility_root_) {
accessibility_root_->Release();
accessibility_root_ = nullptr;
}
UnregisterClass(window_class_name_.c_str(), nullptr);
}
@@ -678,11 +669,18 @@ bool Window::GetHighContrastEnabled() {
}
}
void Window::CreateAccessibilityRootNode() {
if (accessibility_root_) {
accessibility_root_->Release();
void Window::CreateAxFragmentRoot() {
if (ax_fragment_root_) {
return;
}
accessibility_root_ = AccessibilityRootNode::Create();
ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(
window_handle_, GetAxFragmentRootDelegate());
alert_delegate_ =
std::make_unique<AlertPlatformNodeDelegate>(*ax_fragment_root_);
ui::AXPlatformNode* alert_node =
ui::AXPlatformNodeWin::Create(alert_delegate_.get());
alert_node_.reset(static_cast<ui::AXPlatformNodeWin*>(alert_node));
ax_fragment_root_->SetAlertNode(alert_node_.get());
}
} // namespace flutter

View File

@@ -12,8 +12,8 @@
#include <string>
#include <vector>
#include "flutter/shell/platform/common/alert_platform_node_delegate.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/accessibility_root_node.h"
#include "flutter/shell/platform/windows/direct_manipulation.h"
#include "flutter/shell/platform/windows/keyboard_manager.h"
#include "flutter/shell/platform/windows/sequential_id_generator.h"
@@ -22,6 +22,7 @@
#include "flutter/shell/platform/windows/windowsx_shim.h"
#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h"
#include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_win.h"
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
#include "flutter/third_party/accessibility/gfx/native_widget_types.h"
namespace flutter {
@@ -214,6 +215,11 @@ class Window : public KeyboardManager::WindowDelegate {
// Check if the high contrast feature is enabled on the OS
virtual bool GetHighContrastEnabled();
// Creates the ax_fragment_root_, alert_delegate_ and alert_node_ if they do
// not yet exist.
// Once set, they are not reset to nullptr.
void CreateAxFragmentRoot();
// Called to obtain a pointer to the fragment root delegate.
virtual ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() = 0;
@@ -230,9 +236,6 @@ class Window : public KeyboardManager::WindowDelegate {
// Returns the root view accessibility node, or nullptr if none.
virtual gfx::NativeViewAccessible GetNativeViewAccessible() = 0;
// Create the wrapper node.
void CreateAccessibilityRootNode();
// Handles running DirectManipulation on the window to receive trackpad
// gestures.
std::unique_ptr<DirectManipulationOwner> direct_manipulation_owner_;
@@ -240,8 +243,11 @@ class Window : public KeyboardManager::WindowDelegate {
// Called when a theme change message is issued
virtual void OnThemeChange() = 0;
// A parent node wrapping the window root, used for siblings.
AccessibilityRootNode* accessibility_root_;
// Delegate to a alert_node_ used to set the announcement text.
std::unique_ptr<AlertPlatformNodeDelegate> alert_delegate_;
// Accessibility node that represents an alert.
std::unique_ptr<ui::AXPlatformNodeWin> alert_node_;
private:
// Release OS resources associated with window.

View File

@@ -10,11 +10,15 @@
#include <string>
#include <variant>
#include "flutter/shell/platform/common/alert_platform_node_delegate.h"
#include "flutter/shell/platform/common/geometry.h"
#include "flutter/shell/platform/windows/accessibility_root_node.h"
#include "flutter/shell/platform/windows/public/flutter_windows.h"
#include "flutter/shell/platform/windows/window_binding_handler_delegate.h"
namespace ui {
class AXPlatformNodeWin;
}
namespace flutter {
class FlutterWindowsView;
@@ -97,8 +101,11 @@ class WindowBindingHandler {
// Called to set the initial state of accessibility features
virtual void SendInitialAccessibilityFeatures() = 0;
// Returns the wrapper parent accessibility node.
virtual AccessibilityRootNode* GetAccessibilityRootNode() = 0;
// Retrieve the delegate for the alert.
virtual AlertPlatformNodeDelegate* GetAlertDelegate() = 0;
// Retrieve the alert node.
virtual ui::AXPlatformNodeWin* GetAlert() = 0;
};
} // namespace flutter

View File

@@ -289,7 +289,7 @@ class AXFragmentRootMapWin {
AXFragmentRootWin::AXFragmentRootWin(gfx::AcceleratedWidget widget,
AXFragmentRootDelegateWin* delegate)
: widget_(widget), delegate_(delegate) {
: widget_(widget), delegate_(delegate), alert_node_(nullptr) {
platform_node_ = ui::AXFragmentRootPlatformNodeWin::Create(this);
AXFragmentRootMapWin::GetInstance().AddFragmentRoot(widget, this);
}
@@ -331,6 +331,8 @@ int AXFragmentRootWin::GetChildCount() const {
gfx::NativeViewAccessible AXFragmentRootWin::ChildAtIndex(int index) {
if (index == 0) {
return delegate_->GetChildOfAXFragmentRoot();
} else if (index == 1 && alert_node_) {
return alert_node_;
}
return nullptr;
@@ -425,4 +427,18 @@ int AXFragmentRootWin::GetIndexInParentOfChild() const {
return 0;
}
void AXFragmentRootWin::SetAlertNode(AXPlatformNodeWin* alert_node) {
alert_node_ = alert_node;
}
gfx::Rect AXFragmentRootWin::GetBoundsRect(AXCoordinateSystem sys,
AXClippingBehavior clip,
AXOffscreenResult* result) const {
AXPlatformNodeDelegate* child = GetChildNodeDelegate();
if (!child) {
return gfx::Rect();
}
return child->GetBoundsRect(sys, clip, result);
}
} // namespace ui

View File

@@ -13,6 +13,7 @@ namespace ui {
class AXFragmentRootDelegateWin;
class AXFragmentRootPlatformNodeWin;
class AXPlatformNodeWin;
// UI Automation on Windows requires the root of a multi-element provider to
// implement IRawElementProviderFragmentRoot. Our internal accessibility trees
@@ -55,6 +56,16 @@ class AX_EXPORT AXFragmentRootWin : public ui::AXPlatformNodeDelegateBase {
// If a child node is available, return its delegate.
AXPlatformNodeDelegate* GetChildNodeDelegate() const;
// |AXPlatformNodeDelegate|
gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
// alert_node is an AXPlatformNodeWin whose text value can be set by the
// application for the purposes of announcing messages to a screen reader.
// AXFragmentRootWin does not own its alert_node_; it is owned by the object
// with the responsibility of setting its text. In the case of flutter
// windows, this is the Window.
void SetAlertNode(AXPlatformNodeWin* alert_node);
private:
// AXPlatformNodeDelegate overrides.
gfx::NativeViewAccessible GetParent() override;
@@ -65,9 +76,11 @@ class AX_EXPORT AXFragmentRootWin : public ui::AXPlatformNodeDelegateBase {
gfx::NativeViewAccessible HitTestSync(int x, int y) const override;
gfx::NativeViewAccessible GetFocus() override;
const ui::AXUniqueId& GetUniqueId() const override;
gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
AXPlatformNode* GetFromTreeIDAndNodeID(const ui::AXTreeID& ax_tree_id,
int32_t id) override;
gfx::Rect GetBoundsRect(const AXCoordinateSystem acs,
const AXClippingBehavior acb,
AXOffscreenResult* result) const override;
// A fragment root does not correspond to any node in the platform neutral
// accessibility tree. Rather, the fragment root's child is a child of the
@@ -82,6 +95,9 @@ class AX_EXPORT AXFragmentRootWin : public ui::AXPlatformNodeDelegateBase {
AXFragmentRootDelegateWin* const delegate_;
Microsoft::WRL::ComPtr<ui::AXFragmentRootPlatformNodeWin> platform_node_;
ui::AXUniqueId unique_id_;
// Node that presents the alert, if any.
AXPlatformNodeWin* alert_node_;
};
} // namespace ui

View File

@@ -1065,7 +1065,17 @@ IFACEMETHODIMP AXPlatformNodeWin::get_accParent(IDispatch** disp_parent) {
(*disp_parent)->AddRef();
return S_OK;
}
IRawElementProviderFragmentRoot* root;
if (SUCCEEDED(get_FragmentRoot(&root))) {
gfx::NativeViewAccessible parent;
if (SUCCEEDED(root->QueryInterface(IID_PPV_ARGS(&parent)))) {
if (parent && parent != GetNativeViewAccessible()) {
*disp_parent = parent;
parent->AddRef();
return S_OK;
}
}
}
return S_FALSE;
}
@@ -5408,7 +5418,7 @@ AXPlatformNodeWin* AXPlatformNodeWin::GetTargetFromChildID(
AXPlatformNodeBase* base =
FromNativeViewAccessible(node->GetNativeViewAccessible());
if (base && !IsDescendant(base))
if (base && !base->IsDescendantOf(this))
base = nullptr;
return static_cast<AXPlatformNodeWin*>(base);
@@ -5701,4 +5711,28 @@ AXPlatformNodeWin* AXPlatformNodeWin::GetFirstTextOnlyDescendant() {
return nullptr;
}
bool AXPlatformNodeWin::IsDescendantOf(AXPlatformNode* ancestor) const {
if (!ancestor) {
return false;
}
if (AXPlatformNodeBase::IsDescendantOf(ancestor)) {
return true;
}
// Test if the ancestor is an IRawElementProviderFragmentRoot and if it
// matches this node's root fragment.
IRawElementProviderFragmentRoot* root;
if (SUCCEEDED(
const_cast<AXPlatformNodeWin*>(this)->get_FragmentRoot(&root))) {
AXPlatformNodeWin* root_win;
if (SUCCEEDED(root->QueryInterface(__uuidof(AXPlatformNodeWin),
reinterpret_cast<void**>(&root_win)))) {
return ancestor == static_cast<AXPlatformNode*>(root_win);
}
}
return false;
}
} // namespace ui

View File

@@ -473,6 +473,9 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2"))
static std::optional<PROPERTYID> MojoEventToUIAProperty(
ax::mojom::Event event);
// |AXPlatformNodeBase|
bool IsDescendantOf(AXPlatformNode* ancestor) const override;
protected:
// This is hard-coded; all products based on the Chromium engine will have the
// same framework name, so that assistive technology can detect any