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:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user