diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index baecaa91972f5..21ad9f3fc4542 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -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 diff --git a/shell/platform/common/BUILD.gn b/shell/platform/common/BUILD.gn index a1a1817939202..0d45a8ad1eb5f 100644 --- a/shell/platform/common/BUILD.gn +++ b/shell/platform/common/BUILD.gn @@ -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", ] diff --git a/shell/platform/common/alert_platform_node_delegate.cc b/shell/platform/common/alert_platform_node_delegate.cc new file mode 100644 index 0000000000000..3208737eba956 --- /dev/null +++ b/shell/platform/common/alert_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 diff --git a/shell/platform/common/alert_platform_node_delegate.h b/shell/platform/common/alert_platform_node_delegate.h new file mode 100644 index 0000000000000..1cce78484b324 --- /dev/null +++ b/shell/platform/common/alert_platform_node_delegate.h @@ -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_ diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 99be6cf851aef..1f7a850b07135 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -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", diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc deleted file mode 100644 index f0dc623e6f7cf..0000000000000 --- a/shell/platform/windows/accessibility_alert.cc +++ /dev/null @@ -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 diff --git a/shell/platform/windows/accessibility_alert.h b/shell/platform/windows/accessibility_alert.h deleted file mode 100644 index cb5afba907ad6..0000000000000 --- a/shell/platform/windows/accessibility_alert.h +++ /dev/null @@ -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 -#include -#include - -#include - -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, - public IDispatchImpl, - 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_ diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc deleted file mode 100644 index caac7f76493b1..0000000000000 --- a/shell/platform/windows/accessibility_root_node.cc +++ /dev/null @@ -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* instance = nullptr; - HRESULT hr = CComObject::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* instance = nullptr; - HRESULT hr = CComObject::CreateInstance(&instance); - if (!SUCCEEDED(hr) || !instance) { - FML_LOG(FATAL) << "Failed to create accessibility root node"; - } - instance->AddRef(); - return instance; -} - -} // namespace flutter diff --git a/shell/platform/windows/accessibility_root_node.h b/shell/platform/windows/accessibility_root_node.h deleted file mode 100644 index 5d01e83cfe4be..0000000000000 --- a/shell/platform/windows/accessibility_root_node.h +++ /dev/null @@ -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 -#include -#include - -#include - -#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, - public IDispatchImpl, - 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_ diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 2aab541002abd..a1c84c7969eae 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -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 diff --git a/shell/platform/windows/flutter_window.h b/shell/platform/windows/flutter_window.h index f9537885ef890..373ab96ac1cef 100644 --- a/shell/platform/windows/flutter_window.h +++ b/shell/platform/windows/flutter_window.h @@ -151,7 +151,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; diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index 02c57b67629de..99c612db87671 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -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 CreateKeyboardKeyHandler( @@ -415,15 +417,15 @@ TEST(FlutterWindowTest, AlertNode) { std::unique_ptr win32window = std::make_unique(); 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); diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 78cde4a974739..207734f9e2d96 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -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>(); - 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(); diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 1aee3c0ae8d2a..e1b584ddf7398 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -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); -} - -void FlutterWindowsView::NotifyWinEventWrapper(DWORD event, - HWND hwnd, - LONG idObject, - LONG idChild) { - if (hwnd) { - NotifyWinEvent(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(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 diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 083fb13e5ece6..acaa9ad106e0d 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -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 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 diff --git a/shell/platform/windows/testing/mock_window_binding_handler.h b/shell/platform/windows/testing/mock_window_binding_handler.h index 2fc5cbd77e905..709035b3506eb 100644 --- a/shell/platform/windows/testing/mock_window_binding_handler.h +++ b/shell/platform/windows/testing/mock_window_binding_handler.h @@ -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 diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 3a5441afad90f..ceb9cb8379ff9 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -14,6 +14,7 @@ #include +#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 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( - window_handle_, GetAxFragmentRootDelegate()); - } - - // Retrieve UIA object for the root view. - Microsoft::WRL::ComPtr 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 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 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 root(root_view); - accessibility_root_->SetWindow(root_view); - Microsoft::WRL::ComPtr 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( + window_handle_, GetAxFragmentRootDelegate()); + alert_delegate_ = + std::make_unique(*ax_fragment_root_); + ui::AXPlatformNode* alert_node = + ui::AXPlatformNodeWin::Create(alert_delegate_.get()); + alert_node_.reset(static_cast(alert_node)); + ax_fragment_root_->SetAlertNode(alert_node_.get()); } } // namespace flutter diff --git a/shell/platform/windows/window.h b/shell/platform/windows/window.h index 036883ed66a10..1ad35e68e2b86 100644 --- a/shell/platform/windows/window.h +++ b/shell/platform/windows/window.h @@ -12,8 +12,8 @@ #include #include +#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 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 alert_delegate_; + + // Accessibility node that represents an alert. + std::unique_ptr alert_node_; private: // Release OS resources associated with window. diff --git a/shell/platform/windows/window_binding_handler.h b/shell/platform/windows/window_binding_handler.h index 47b8ecf0e048d..e13d7a138cfa5 100644 --- a/shell/platform/windows/window_binding_handler.h +++ b/shell/platform/windows/window_binding_handler.h @@ -10,11 +10,15 @@ #include #include +#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 diff --git a/third_party/accessibility/ax/platform/ax_fragment_root_win.cc b/third_party/accessibility/ax/platform/ax_fragment_root_win.cc index d6abe02c3e2c7..e6ef519656473 100644 --- a/third_party/accessibility/ax/platform/ax_fragment_root_win.cc +++ b/third_party/accessibility/ax/platform/ax_fragment_root_win.cc @@ -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 diff --git a/third_party/accessibility/ax/platform/ax_fragment_root_win.h b/third_party/accessibility/ax/platform/ax_fragment_root_win.h index ec357d4dffbff..fe3699cf211ae 100644 --- a/third_party/accessibility/ax/platform/ax_fragment_root_win.h +++ b/third_party/accessibility/ax/platform/ax_fragment_root_win.h @@ -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 platform_node_; ui::AXUniqueId unique_id_; + + // Node that presents the alert, if any. + AXPlatformNodeWin* alert_node_; }; } // namespace ui diff --git a/third_party/accessibility/ax/platform/ax_platform_node_win.cc b/third_party/accessibility/ax/platform/ax_platform_node_win.cc index 097379a4f672f..b00f5d2c58eaf 100644 --- a/third_party/accessibility/ax/platform/ax_platform_node_win.cc +++ b/third_party/accessibility/ax/platform/ax_platform_node_win.cc @@ -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(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(this)->get_FragmentRoot(&root))) { + AXPlatformNodeWin* root_win; + if (SUCCEEDED(root->QueryInterface(__uuidof(AXPlatformNodeWin), + reinterpret_cast(&root_win)))) { + return ancestor == static_cast(root_win); + } + } + + return false; +} + } // namespace ui diff --git a/third_party/accessibility/ax/platform/ax_platform_node_win.h b/third_party/accessibility/ax/platform/ax_platform_node_win.h index c2dba4fcd71ec..cf977b4c7f845 100644 --- a/third_party/accessibility/ax/platform/ax_platform_node_win.h +++ b/third_party/accessibility/ax/platform/ax_platform_node_win.h @@ -473,6 +473,9 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) static std::optional 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