From 9def66493497c5d580f0e93233c276dc366724c3 Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 18 Oct 2022 15:32:14 -0400 Subject: [PATCH 01/21] Pickup announcement messages --- .../windows/flutter_windows_engine.cc | 46 +++++++++++++++++++ .../platform/windows/flutter_windows_engine.h | 2 + 2 files changed, 48 insertions(+) diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index b75764cf68d45..19b9bae2b0be5 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -14,6 +14,7 @@ #include "flutter/fml/paths.h" #include "flutter/fml/platform/win/wstring_conversion.h" #include "flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h" +#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h" #include "flutter/shell/platform/common/path_utils.h" #include "flutter/shell/platform/windows/accessibility_bridge_delegate_windows.h" #include "flutter/shell/platform/windows/flutter_windows_view.h" @@ -23,6 +24,8 @@ // winbase.h defines GetCurrentTime as a macro. #undef GetCurrentTime +static constexpr char kAccessibilityChannelName[] = "flutter/accessibility"; + namespace flutter { namespace { @@ -183,6 +186,10 @@ FlutterWindowsEngine::FlutterWindowsEngine( messenger_wrapper_ = std::make_unique(messenger_.get()); message_dispatcher_ = std::make_unique(messenger_.get()); + message_dispatcher_->SetMessageCallback(kAccessibilityChannelName, [](FlutterDesktopMessengerRef messenger, const FlutterDesktopMessage* message, void* data){ + FlutterWindowsEngine* engine = static_cast(data); + engine->HandleAccessibilityMessage(messenger, message); + }, static_cast(this)); FlutterWindowsTextureRegistrar::ResolveGlFunctions(gl_procs_); texture_registrar_ = @@ -660,4 +667,43 @@ int FlutterWindowsEngine::EnabledAccessibilityFeatures() const { return flags; } +void FlutterWindowsEngine::HandleAccessibilityMessage(FlutterDesktopMessengerRef messenger, const FlutterDesktopMessage* message) { + const auto& codec = StandardMessageCodec::GetInstance(); + auto data = codec.DecodeMessage(message->message, message->message_size); + EncodableMap map = std::get(*data); + std::string type = std::get(map.at(EncodableValue("type"))); + FML_LOG(ERROR) << "Handling a11y call of type " << type; + if (type.compare("announce") == 0) { + if (semantics_enabled_) { + EncodableMap data_map = std::get(map.at(EncodableValue("data"))); + std::string text = std::get(data_map.at(EncodableValue("message"))); + FML_LOG(ERROR) << "Announcing " << text; + FlutterSemanticsNode announcement = { + .struct_size = sizeof(FlutterSemanticsNode), + .id = -1, + .flags = static_cast(0), + .actions = static_cast(0), + .text_selection_base = -1, + .text_selection_extent = -1, + .label = text.c_str(), + .hint = "", + .value = "", + .increased_value = "", + .decreased_value = "", + .child_count = 0, + .children_in_traversal_order = nullptr, + .custom_accessibility_actions_count = 0, + .tooltip = "" + }; + accessibility_bridge_->AddFlutterSemanticsNodeUpdate(&announcement); + accessibility_bridge_->CommitUpdates(); + auto root = std::static_pointer_cast(accessibility_bridge_->GetFlutterPlatformNodeDelegateFromID(AccessibilityBridge::kRootNodeId).lock()); + FML_LOG(ERROR) << "Got root? " << (!!root); + int32_t root_id = root->GetAXNode()->id(); + FML_LOG(ERROR) << "Root id = " << root_id; + } + } + SendPlatformMessageResponse(message->response_handle, reinterpret_cast(""), 0); +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index a5dfe145e3021..09add7386a96f 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -254,6 +254,8 @@ class FlutterWindowsEngine { // system changes. void SendSystemLocales(); + void HandleAccessibilityMessage(FlutterDesktopMessengerRef messenger, const FlutterDesktopMessage* message); + // The handle to the embedder.h engine instance. FLUTTER_API_SYMBOL(FlutterEngine) engine_ = nullptr; From cc0913c1ad00c63039c086715154f3b1eabbbe6d Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 21 Oct 2022 10:18:31 -0400 Subject: [PATCH 02/21] Create files necessary for alert node --- shell/platform/windows/BUILD.gn | 5 +++++ shell/platform/windows/accessibility_alert.cc | 0 shell/platform/windows/accessibility_alert.h | 0 .../windows/accessibility_root_node.cc | 0 .../platform/windows/accessibility_root_node.h | 0 .../accessibility_root_node_unittests.cc | 0 .../platform/windows/flutter_windows_engine.cc | 18 +++++++++++++++--- 7 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 shell/platform/windows/accessibility_alert.cc create mode 100644 shell/platform/windows/accessibility_alert.h create mode 100644 shell/platform/windows/accessibility_root_node.cc create mode 100644 shell/platform/windows/accessibility_root_node.h create mode 100644 shell/platform/windows/accessibility_root_node_unittests.cc diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 9f6b3b1b7fdbd..3eb10c073bf61 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -38,8 +38,12 @@ source_set("flutter_windows_headers") { source_set("flutter_windows_source") { # Common Windows sources. sources = [ + "accessibility_alert.cc", + "accessibility_alert.h", "accessibility_bridge_delegate_windows.cc", "accessibility_bridge_delegate_windows.h", + "accessibility_root_node.cc", + "accessibility_root_node.h", "angle_surface_manager.cc", "angle_surface_manager.h", "cursor_handler.cc", @@ -172,6 +176,7 @@ executable("flutter_windows_unittests") { # Common Windows test sources. sources = [ "accessibility_bridge_delegate_windows_unittests.cc", + "accessibility_root_node_unittests.cc", "direct_manipulation_unittests.cc", "dpi_utils_unittests.cc", "flutter_project_bundle_unittests.cc", diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/shell/platform/windows/accessibility_alert.h b/shell/platform/windows/accessibility_alert.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/shell/platform/windows/accessibility_root_node.h b/shell/platform/windows/accessibility_root_node.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/shell/platform/windows/accessibility_root_node_unittests.cc b/shell/platform/windows/accessibility_root_node_unittests.cc new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 19b9bae2b0be5..d7e27a4ad9554 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -20,6 +20,7 @@ #include "flutter/shell/platform/windows/flutter_windows_view.h" #include "flutter/shell/platform/windows/system_utils.h" #include "flutter/shell/platform/windows/task_runner.h" +#include "flutter/third_party/accessibility/ax/ax_node.h" // winbase.h defines GetCurrentTime as a macro. #undef GetCurrentTime @@ -678,7 +679,7 @@ void FlutterWindowsEngine::HandleAccessibilityMessage(FlutterDesktopMessengerRef EncodableMap data_map = std::get(map.at(EncodableValue("data"))); std::string text = std::get(data_map.at(EncodableValue("message"))); FML_LOG(ERROR) << "Announcing " << text; - FlutterSemanticsNode announcement = { + /*FlutterSemanticsNode announcement = { .struct_size = sizeof(FlutterSemanticsNode), .id = -1, .flags = static_cast(0), @@ -696,11 +697,22 @@ void FlutterWindowsEngine::HandleAccessibilityMessage(FlutterDesktopMessengerRef .tooltip = "" }; accessibility_bridge_->AddFlutterSemanticsNodeUpdate(&announcement); - accessibility_bridge_->CommitUpdates(); + accessibility_bridge_->CommitUpdates();*/ auto root = std::static_pointer_cast(accessibility_bridge_->GetFlutterPlatformNodeDelegateFromID(AccessibilityBridge::kRootNodeId).lock()); FML_LOG(ERROR) << "Got root? " << (!!root); - int32_t root_id = root->GetAXNode()->id(); + auto root_node = root->GetAXNode(); + int32_t root_id = root_node->id(); FML_LOG(ERROR) << "Root id = " << root_id; + for (auto it = root_node->UnignoredChildrenBegin(); it != root_node->UnignoredChildrenEnd(); ++it) { + FML_LOG(ERROR) << "ID of child: " << it->id(); + } + auto owner_tree = root_node->tree(); + auto siblings = root_node->children(); + int32_t index_in_parent = siblings.size(); + int32_t uindex = root_node->GetUnignoredChildCount(); + ui::AXNode* node = new ui::AXNode(owner_tree, root_node, -1, index_in_parent, uindex); + siblings.push_back(node); + root_node->SwapChildren(&siblings); } } SendPlatformMessageResponse(message->response_handle, reinterpret_cast(""), 0); From 0ead65662015c2221b21f89bc016d52207d7e2a0 Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 21 Oct 2022 10:39:33 -0400 Subject: [PATCH 03/21] Create skeleton of classes used --- shell/platform/windows/BUILD.gn | 1 - shell/platform/windows/accessibility_alert.cc | 9 +++++++ shell/platform/windows/accessibility_alert.h | 25 +++++++++++++++++++ .../windows/accessibility_root_node.cc | 9 +++++++ .../windows/accessibility_root_node.h | 25 +++++++++++++++++++ .../accessibility_root_node_unittests.cc | 0 6 files changed, 68 insertions(+), 1 deletion(-) delete mode 100644 shell/platform/windows/accessibility_root_node_unittests.cc diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 3eb10c073bf61..1ed92aec50f1d 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -176,7 +176,6 @@ executable("flutter_windows_unittests") { # Common Windows test sources. sources = [ "accessibility_bridge_delegate_windows_unittests.cc", - "accessibility_root_node_unittests.cc", "direct_manipulation_unittests.cc", "dpi_utils_unittests.cc", "flutter_project_bundle_unittests.cc", diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc index e69de29bb2d1d..cd20d75c23221 100644 --- a/shell/platform/windows/accessibility_alert.cc +++ b/shell/platform/windows/accessibility_alert.cc @@ -0,0 +1,9 @@ +// 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" + +namespace flutter { + +} diff --git a/shell/platform/windows/accessibility_alert.h b/shell/platform/windows/accessibility_alert.h index e69de29bb2d1d..e8f2b0a9ac20c 100644 --- a/shell/platform/windows/accessibility_alert.h +++ b/shell/platform/windows/accessibility_alert.h @@ -0,0 +1,25 @@ +// 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 + +namespace flutter { + +class AccessibilityAlert : public CComObjectRootEx, + public IAccessible { + public: + BEGIN_COM_MAP(AccessibilityAlert) + COM_INTERFACE_ENTRY(IAccessible) + END_COM_MAP() + +}; + +} + +#endif diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index e69de29bb2d1d..f22c70be1fdc1 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -0,0 +1,9 @@ +// 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" + +namespace flutter { + +} diff --git a/shell/platform/windows/accessibility_root_node.h b/shell/platform/windows/accessibility_root_node.h index e69de29bb2d1d..11055206dbb21 100644 --- a/shell/platform/windows/accessibility_root_node.h +++ b/shell/platform/windows/accessibility_root_node.h @@ -0,0 +1,25 @@ +// 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 + +namespace flutter { + +class AccessibilityRootNode : public CComObjectRootEx, + public IAccessible { + public: + BEGIN_COM_MAP(AccessibilityRootNode) + COM_INTERFACE_ENTRY(IAccessible) + END_COM_MAP() + +}; + +} + +#endif diff --git a/shell/platform/windows/accessibility_root_node_unittests.cc b/shell/platform/windows/accessibility_root_node_unittests.cc deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 03acbea313dde0d6fb50cbc2118644b7d169573a Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 21 Oct 2022 13:20:39 -0400 Subject: [PATCH 04/21] Hold my progress --- shell/platform/windows/accessibility_alert.h | 82 +++++++ .../windows/accessibility_root_node.cc | 225 ++++++++++++++++++ .../windows/accessibility_root_node.h | 96 ++++++++ shell/platform/windows/flutter_window.cc | 4 + shell/platform/windows/flutter_window.h | 3 + .../testing/mock_window_binding_handler.h | 1 + shell/platform/windows/window.cc | 6 + shell/platform/windows/window.h | 4 + .../platform/windows/window_binding_handler.h | 4 + 9 files changed, 425 insertions(+) diff --git a/shell/platform/windows/accessibility_alert.h b/shell/platform/windows/accessibility_alert.h index e8f2b0a9ac20c..f065ebf89f391 100644 --- a/shell/platform/windows/accessibility_alert.h +++ b/shell/platform/windows/accessibility_alert.h @@ -9,14 +9,96 @@ #include #include +#include + namespace flutter { +// An IAccessible node representing an alert read to the screen reader. class AccessibilityAlert : public CComObjectRootEx, public IAccessible { public: BEGIN_COM_MAP(AccessibilityAlert) COM_INTERFACE_ENTRY(IAccessible) 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; + + // Sets the text of this alert to the provided message. + void SetText(const std::u16string& text); + + private: + std::u16string text_; }; diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index f22c70be1fdc1..b2f82e23ce91b 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -4,6 +4,231 @@ #include "flutter/shell/platform/windows/accessibility_root_node.h" +static constexpr LONG kWindowChildId = 1; +static constexpr LONG kAlertChildId = 2; +static constexpr LONG kInvalidChildId = 3; + namespace flutter { +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 FALSE; + } + 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_; + } + 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 window_accessible_->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 window_accessible_->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 == 1) { + *disp_child = window_accessible_; + } else if (child_id == 2 && alert_accessible_) { + *disp_child = alert_accessible_; + } 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 window_accessible_->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 window_accessible_->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 window_accessible_->get_accKeyboardShortcut(var_id, acc_key); + } + return E_FAIL; +} + +IFACEMETHODIMP AccessibilityRootNode::get_accName(VARIANT var_id, BSTR* name_bstr) { + IAccessible* target; + if ((target = GetTargetAndChildID(&var_id))) { + return window_accessible_->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 window_accessible_->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 window_accessible_->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 window_accessible_->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 window_accessible_->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 window_accessible_->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; +} + } diff --git a/shell/platform/windows/accessibility_root_node.h b/shell/platform/windows/accessibility_root_node.h index 11055206dbb21..fc055d16c6b2a 100644 --- a/shell/platform/windows/accessibility_root_node.h +++ b/shell/platform/windows/accessibility_root_node.h @@ -9,8 +9,13 @@ #include #include +#include + +#include "flutter/shell/platform/windows/accessibility_alert.h" + namespace flutter { +// A parent node that wraps the window IAccessible node. class AccessibilityRootNode : public CComObjectRootEx, public IAccessible { public: @@ -18,6 +23,97 @@ class AccessibilityRootNode : public CComObjectRootEx, COM_INTERFACE_ENTRY(IAccessible) 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; + + AccessibilityRootNode() = default; + ~AccessibilityRootNode(); + + void SetWindow(IAccessible* window); + + void SetAlert(AccessibilityAlert* alert); + + inline AccessibilityAlert* GetAlert() {return alert_accessible_;} + + private: + // Helper method to redirect method calls to the contained window. + IAccessible* GetTargetAndChildID(VARIANT* var_id); + + IAccessible* window_accessible_; + + AccessibilityAlert* alert_accessible_; + }; } diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 968744209f214..1a335a77a05fc 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -291,4 +291,8 @@ void FlutterWindow::SendInitialAccessibilityFeatures() { OnThemeChange(); } +AccessibilityRootNode* FlutterWindow::GetAccessibilityRootNode() { + return accessibility_root_; +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_window.h b/shell/platform/windows/flutter_window.h index 29c9c69554638..5af03bad9c213 100644 --- a/shell/platform/windows/flutter_window.h +++ b/shell/platform/windows/flutter_window.h @@ -148,6 +148,9 @@ class FlutterWindow : public Window, public WindowBindingHandler { // |WindowBindingHandler| void SendInitialAccessibilityFeatures() override; + // |WindowBindingHandler| + AccessibilityRootNode* GetAccessibilityRootNode() override; + private: // A pointer to a FlutterWindowsView that can be used to update engine // windowing and input state. diff --git a/shell/platform/windows/testing/mock_window_binding_handler.h b/shell/platform/windows/testing/mock_window_binding_handler.h index dc8ea7433fa17..6a191b0e1817b 100644 --- a/shell/platform/windows/testing/mock_window_binding_handler.h +++ b/shell/platform/windows/testing/mock_window_binding_handler.h @@ -37,6 +37,7 @@ 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*()); }; } // namespace testing diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 913a3f85fef07..99baaa9d0fabc 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -212,6 +212,7 @@ LRESULT Window::OnGetObject(UINT const message, } else if (is_msaa_request && root_view) { // Return the IAccessible for the root view. Microsoft::WRL::ComPtr root(root_view); + // TODO(schectman): wrap returned accessible in AccessibilityRootNode LRESULT lresult = LresultFromObject(IID_IAccessible, wparam, root.Get()); return lresult; } @@ -596,6 +597,11 @@ void Window::Destroy() { window_handle_ = nullptr; } + if (accessibility_root_) { + accessibility_root_->Release(); + accessibility_root_ = nullptr; + } + UnregisterClass(window_class_name_.c_str(), nullptr); } diff --git a/shell/platform/windows/window.h b/shell/platform/windows/window.h index e31be30f1619d..fed064f347ee5 100644 --- a/shell/platform/windows/window.h +++ b/shell/platform/windows/window.h @@ -14,6 +14,7 @@ #include #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" @@ -230,6 +231,9 @@ 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_; + private: // Release OS resources associated with window. void Destroy(); diff --git a/shell/platform/windows/window_binding_handler.h b/shell/platform/windows/window_binding_handler.h index abde74e59822f..13d04eb604b1f 100644 --- a/shell/platform/windows/window_binding_handler.h +++ b/shell/platform/windows/window_binding_handler.h @@ -11,6 +11,7 @@ #include #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" @@ -92,6 +93,9 @@ 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; }; } // namespace flutter From 5aa08284a926abf8b3407aa3e20f1961e31c9fb6 Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 21 Oct 2022 16:48:10 -0400 Subject: [PATCH 05/21] Announcements get read :) --- shell/platform/windows/accessibility_alert.cc | 151 ++++++++++++++++++ shell/platform/windows/accessibility_alert.h | 15 +- .../windows/accessibility_root_node.cc | 56 +++++-- .../windows/accessibility_root_node.h | 7 +- shell/platform/windows/flutter_window.cc | 1 + .../windows/flutter_windows_engine.cc | 6 +- .../platform/windows/flutter_windows_view.cc | 7 + shell/platform/windows/flutter_windows_view.h | 3 + shell/platform/windows/window.cc | 21 ++- 9 files changed, 243 insertions(+), 24 deletions(-) diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc index cd20d75c23221..72e5b50b4a486 100644 --- a/shell/platform/windows/accessibility_alert.cc +++ b/shell/platform/windows/accessibility_alert.cc @@ -4,6 +4,157 @@ #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) {} + +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 the specified object's current screen location. +IFACEMETHODIMP AccessibilityAlert::accLocation(LONG* physical_pixel_left, + LONG* physical_pixel_top, + LONG* width, + LONG* height, + VARIANT var_id) { + return E_NOTIMPL; +} + +// Traverses to another UI element and retrieves the object. +IFACEMETHODIMP AccessibilityAlert::accNavigate(LONG nav_dir, + VARIANT start, + VARIANT* end) { + end->vt = VT_EMPTY; + return E_NOTIMPL; +} + +// 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 a string that describes the object's default action. +IFACEMETHODIMP AccessibilityAlert::get_accDefaultAction(VARIANT var_id, + BSTR* default_action) { + *default_action = nullptr; + return E_NOTIMPL; +} + +// Retrieves the tooltip description. +IFACEMETHODIMP AccessibilityAlert::get_accDescription(VARIANT var_id, BSTR* desc) { + *desc = nullptr; + return E_NOTIMPL; +} + +// Retrieves the object that has the keyboard focus. +IFACEMETHODIMP AccessibilityAlert::get_accFocus(VARIANT* focus_child) { + focus_child->vt = VT_EMPTY; + return E_NOTIMPL; +} + +// Retrieves the specified object's shortcut. +IFACEMETHODIMP AccessibilityAlert::get_accKeyboardShortcut(VARIANT var_id, + BSTR* access_key) { + *access_key = nullptr; + return E_NOTIMPL; +} + +// 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_; + //return S_OK; + *disp_parent = nullptr; + return E_NOTIMPL; +} + +// 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; +} +IFACEMETHODIMP AccessibilityAlert::put_accValue(VARIANT var_id, BSTR new_value) { + return E_NOTIMPL; +} + +// 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::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; +} + +void AccessibilityAlert::SetText(const std::wstring& text) { + text_ = text; +} + +void AccessibilityAlert::SetParent(AccessibilityRootNode* parent) { + parent_ = parent; +} + } diff --git a/shell/platform/windows/accessibility_alert.h b/shell/platform/windows/accessibility_alert.h index f065ebf89f391..a8fada247ff29 100644 --- a/shell/platform/windows/accessibility_alert.h +++ b/shell/platform/windows/accessibility_alert.h @@ -13,9 +13,11 @@ namespace flutter { +class AccessibilityRootNode; + // An IAccessible node representing an alert read to the screen reader. class AccessibilityAlert : public CComObjectRootEx, - public IAccessible { + public IDispatchImpl { public: BEGIN_COM_MAP(AccessibilityAlert) COM_INTERFACE_ENTRY(IAccessible) @@ -94,11 +96,18 @@ class AccessibilityAlert : public CComObjectRootEx, LONG* topic_id) override; IFACEMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override; + AccessibilityAlert(); + ~AccessibilityAlert() = default; + // Sets the text of this alert to the provided message. - void SetText(const std::u16string& text); + void SetText(const std::wstring& text); + + void SetParent(AccessibilityRootNode* parent); private: - std::u16string text_; + std::wstring text_; + + AccessibilityRootNode* parent_; }; diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index b2f82e23ce91b..5c0d5f3a2b794 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -4,11 +4,15 @@ #include "flutter/shell/platform/windows/accessibility_root_node.h" +#include "flutter/fml/logging.h" + +namespace flutter { + static constexpr LONG kWindowChildId = 1; -static constexpr LONG kAlertChildId = 2; static constexpr LONG kInvalidChildId = 3; -namespace flutter { +AccessibilityRootNode::AccessibilityRootNode() : + alert_accessible_(nullptr) {} AccessibilityRootNode::~AccessibilityRootNode() { if (alert_accessible_) { @@ -66,7 +70,7 @@ IFACEMETHODIMP AccessibilityRootNode::accLocation(LONG* physical_pixel_left, VARIANT var_id) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->accLocation(physical_pixel_left, physical_pixel_top, width, height, var_id); + return target->accLocation(physical_pixel_left, physical_pixel_top, width, height, var_id); } return S_FALSE; } @@ -76,7 +80,7 @@ IFACEMETHODIMP AccessibilityRootNode::accNavigate(LONG nav_dir, VARIANT* end) { IAccessible* target; if ((target = GetTargetAndChildID(&start))) { - return window_accessible_->accNavigate(nav_dir, start, end); + return target->accNavigate(nav_dir, start, end); } return S_FALSE; } @@ -91,10 +95,12 @@ IFACEMETHODIMP AccessibilityRootNode::get_accChild(VARIANT var_child, *disp_child = this; } else if (!window_accessible_) { return E_FAIL; - } else if (child_id == 1) { + } else if (child_id == kWindowChildId) { *disp_child = window_accessible_; - } else if (child_id == 2 && alert_accessible_) { + } else if (child_id == kAlertChildId && alert_accessible_) { *disp_child = alert_accessible_; + } else if (child_id < 0) { + return window_accessible_->get_accChild(var_child, disp_child); } else { return E_FAIL; } @@ -118,7 +124,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accDefaultAction(VARIANT var_id, BSTR* def_action) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->get_accDefaultAction(var_id, def_action); + return target->get_accDefaultAction(var_id, def_action); } *def_action = nullptr; return S_FALSE; @@ -128,7 +134,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accDescription(VARIANT var_id, BSTR* desc) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->get_accDescription(var_id, desc); + return target->get_accDescription(var_id, desc); } return E_FAIL; } @@ -145,12 +151,17 @@ IFACEMETHODIMP AccessibilityRootNode::get_accKeyboardShortcut(VARIANT var_id, BSTR* acc_key) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->get_accKeyboardShortcut(var_id, acc_key); + 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 window_accessible_->get_accName(var_id, name_bstr); @@ -165,7 +176,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accParent(IDispatch** disp_parent) { IFACEMETHODIMP AccessibilityRootNode::get_accRole(VARIANT var_id, VARIANT* role) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->get_accRole(var_id, role); + return target->get_accRole(var_id, role); } return E_FAIL; } @@ -173,7 +184,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accRole(VARIANT var_id, VARIANT* role) IFACEMETHODIMP AccessibilityRootNode::get_accState(VARIANT var_id, VARIANT* state) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->get_accState(var_id, state); + return target->get_accState(var_id, state); } return E_FAIL; } @@ -189,7 +200,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accHelp(VARIANT var_id, BSTR* help) { IFACEMETHODIMP AccessibilityRootNode::get_accValue(VARIANT var_id, BSTR* value) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->get_accValue(var_id, value); + return target->get_accValue(var_id, value); } return E_FAIL; } @@ -197,7 +208,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accValue(VARIANT var_id, BSTR* value) IFACEMETHODIMP AccessibilityRootNode::put_accValue(VARIANT var_id, BSTR new_value) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->put_accValue(var_id, new_value); + return target->put_accValue(var_id, new_value); } return E_FAIL; } @@ -210,7 +221,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accSelection(VARIANT* selected) { IFACEMETHODIMP AccessibilityRootNode::accSelect(LONG flagsSelect, VARIANT var_id) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->accSelect(flagsSelect, var_id); + return target->accSelect(flagsSelect, var_id); } return E_FAIL; } @@ -231,4 +242,21 @@ IFACEMETHODIMP AccessibilityRootNode::put_accName(VARIANT var_id, BSTR put_name) return E_NOTIMPL; } +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->SetParent(this); + alert_accessible_ = instance; + } + return alert_accessible_; +} + } diff --git a/shell/platform/windows/accessibility_root_node.h b/shell/platform/windows/accessibility_root_node.h index fc055d16c6b2a..3978e052efece 100644 --- a/shell/platform/windows/accessibility_root_node.h +++ b/shell/platform/windows/accessibility_root_node.h @@ -17,8 +17,9 @@ namespace flutter { // A parent node that wraps the window IAccessible node. class AccessibilityRootNode : public CComObjectRootEx, - public IAccessible { + public IDispatchImpl { public: + static constexpr LONG kAlertChildId = 2; BEGIN_COM_MAP(AccessibilityRootNode) COM_INTERFACE_ENTRY(IAccessible) END_COM_MAP() @@ -97,14 +98,14 @@ class AccessibilityRootNode : public CComObjectRootEx, LONG* topic_id) override; IFACEMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override; - AccessibilityRootNode() = default; + AccessibilityRootNode(); ~AccessibilityRootNode(); void SetWindow(IAccessible* window); void SetAlert(AccessibilityAlert* alert); - inline AccessibilityAlert* GetAlert() {return alert_accessible_;} + AccessibilityAlert* GetOrCreateAlert(); private: // Helper method to redirect method calls to the contained window. diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 1a335a77a05fc..74912cd181901 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -292,6 +292,7 @@ void FlutterWindow::SendInitialAccessibilityFeatures() { } AccessibilityRootNode* FlutterWindow::GetAccessibilityRootNode() { + FML_LOG(ERROR) << "Returning accessibility_root_"; return accessibility_root_; } diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index d7e27a4ad9554..b67ba01c526d3 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -697,7 +697,7 @@ void FlutterWindowsEngine::HandleAccessibilityMessage(FlutterDesktopMessengerRef .tooltip = "" }; accessibility_bridge_->AddFlutterSemanticsNodeUpdate(&announcement); - accessibility_bridge_->CommitUpdates();*/ + accessibility_bridge_->CommitUpdates(); auto root = std::static_pointer_cast(accessibility_bridge_->GetFlutterPlatformNodeDelegateFromID(AccessibilityBridge::kRootNodeId).lock()); FML_LOG(ERROR) << "Got root? " << (!!root); auto root_node = root->GetAXNode(); @@ -712,7 +712,9 @@ void FlutterWindowsEngine::HandleAccessibilityMessage(FlutterDesktopMessengerRef int32_t uindex = root_node->GetUnignoredChildCount(); ui::AXNode* node = new ui::AXNode(owner_tree, root_node, -1, index_in_parent, uindex); siblings.push_back(node); - root_node->SwapChildren(&siblings); + root_node->SwapChildren(&siblings);*/ + std::wstring wide_text = fml::Utf8ToWideString(text); + view_->AnnounceAlert(wide_text); } } SendPlatformMessageResponse(message->response_handle, reinterpret_cast(""), 0); diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index e212b6cf6140a..43ffea8448fc0 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -656,4 +656,11 @@ FlutterWindowsEngine* FlutterWindowsView::GetEngine() { return engine_.get(); } +void FlutterWindowsView::AnnounceAlert(const std::wstring& text) { + AccessibilityAlert* alert = binding_handler_->GetAccessibilityRootNode()->GetOrCreateAlert(); + alert->SetText(text); + HWND hwnd = GetPlatformWindow(); + NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT, AccessibilityRootNode::kAlertChildId); +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 6075903309479..4918b4db3fa5e 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -90,6 +90,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // Send the initial accessibility features to the window void SendInitialAccessibilityFeatures(); + // Set the text of the alert, and create it if it does not yet exist. + void AnnounceAlert(const std::wstring& text); + // |WindowBindingHandlerDelegate| void UpdateHighContrastEnabled(bool enabled) override; diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 99baaa9d0fabc..8284e2dc08d73 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -15,6 +15,7 @@ #include #include "flutter/shell/platform/windows/dpi_utils.h" +#include "flutter/third_party/accessibility/base/win/atl_module.h" namespace flutter { @@ -58,7 +59,8 @@ Window::Window(std::unique_ptr windows_proc_table, std::unique_ptr text_input_manager) : touch_id_generator_(kMinTouchDeviceId, kMaxTouchDeviceId), windows_proc_table_(std::move(windows_proc_table)), - text_input_manager_(std::move(text_input_manager)) { + text_input_manager_(std::move(text_input_manager)), + accessibility_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 // kWmDpiChangedBeforeParent message. @@ -210,8 +212,22 @@ LRESULT Window::OnGetObject(UINT const message, // TODO(cbracken): https://github.com/flutter/flutter/issues/94782 // Implement when we adopt UIA support. } else if (is_msaa_request && root_view) { + // Create the accessibility root if it does not already exist. + if (!accessibility_root_) { + ui::win::CreateATLModuleIfNeeded(); + CComObject* instance = nullptr; + HRESULT hr = CComObject::CreateInstance(&instance); + if (!SUCCEEDED(hr)) { + FML_LOG(FATAL) << "Failed to create accessibility root node"; + } + instance->AddRef(); + accessibility_root_ = instance; + FML_LOG(ERROR) << "Creating accessibility root"; + } // Return the IAccessible for the root view. - Microsoft::WRL::ComPtr root(root_view); + //Microsoft::WRL::ComPtr root(root_view); + accessibility_root_->SetWindow(root_view); + Microsoft::WRL::ComPtr root(accessibility_root_); // TODO(schectman): wrap returned accessible in AccessibilityRootNode LRESULT lresult = LresultFromObject(IID_IAccessible, wparam, root.Get()); return lresult; @@ -598,6 +614,7 @@ void Window::Destroy() { } if (accessibility_root_) { + FML_LOG(ERROR) << "Destroying accessibility_root_!!\n"; accessibility_root_->Release(); accessibility_root_ = nullptr; } From 1d3b910ab186bc5eb15ff695a18bd263205e3c34 Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 21 Oct 2022 17:45:26 -0400 Subject: [PATCH 06/21] Reads announcements --- shell/platform/windows/accessibility_alert.cc | 15 ++++---- .../windows/accessibility_root_node.cc | 1 + shell/platform/windows/flutter_window.cc | 1 - .../windows/flutter_windows_engine.cc | 36 ------------------- shell/platform/windows/window.cc | 2 -- 5 files changed, 10 insertions(+), 45 deletions(-) diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc index 72e5b50b4a486..26e7741dd9659 100644 --- a/shell/platform/windows/accessibility_alert.cc +++ b/shell/platform/windows/accessibility_alert.cc @@ -66,8 +66,8 @@ IFACEMETHODIMP AccessibilityAlert::get_accDefaultAction(VARIANT var_id, // Retrieves the tooltip description. IFACEMETHODIMP AccessibilityAlert::get_accDescription(VARIANT var_id, BSTR* desc) { - *desc = nullptr; - return E_NOTIMPL; + *desc = SysAllocString(text_.c_str()); + return S_OK; } // Retrieves the object that has the keyboard focus. @@ -91,10 +91,13 @@ IFACEMETHODIMP AccessibilityAlert::get_accName(VARIANT var_id, BSTR* name) { // Retrieves the IDispatch interface of the object's parent. IFACEMETHODIMP AccessibilityAlert::get_accParent(IDispatch** disp_parent) { - //*disp_parent = parent_; - //return S_OK; - *disp_parent = nullptr; - return E_NOTIMPL; + *disp_parent = parent_; + if (*disp_parent) { + (*disp_parent)->AddRef(); + } + return S_OK; + //*disp_parent = nullptr; + //return E_NOTIMPL; } // Retrieves information describing the role of the specified object. diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index 5c0d5f3a2b794..47a8ea0db9f45 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -253,6 +253,7 @@ AccessibilityAlert* AccessibilityRootNode::GetOrCreateAlert() { if (!SUCCEEDED(hr)) { FML_LOG(FATAL) << "Failed to create alert accessible"; } + instance->AddRef(); instance->SetParent(this); alert_accessible_ = instance; } diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 74912cd181901..1a335a77a05fc 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -292,7 +292,6 @@ void FlutterWindow::SendInitialAccessibilityFeatures() { } AccessibilityRootNode* FlutterWindow::GetAccessibilityRootNode() { - FML_LOG(ERROR) << "Returning accessibility_root_"; return accessibility_root_; } diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index b67ba01c526d3..a3281622c8295 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -673,46 +673,10 @@ void FlutterWindowsEngine::HandleAccessibilityMessage(FlutterDesktopMessengerRef auto data = codec.DecodeMessage(message->message, message->message_size); EncodableMap map = std::get(*data); std::string type = std::get(map.at(EncodableValue("type"))); - FML_LOG(ERROR) << "Handling a11y call of type " << type; if (type.compare("announce") == 0) { if (semantics_enabled_) { EncodableMap data_map = std::get(map.at(EncodableValue("data"))); std::string text = std::get(data_map.at(EncodableValue("message"))); - FML_LOG(ERROR) << "Announcing " << text; - /*FlutterSemanticsNode announcement = { - .struct_size = sizeof(FlutterSemanticsNode), - .id = -1, - .flags = static_cast(0), - .actions = static_cast(0), - .text_selection_base = -1, - .text_selection_extent = -1, - .label = text.c_str(), - .hint = "", - .value = "", - .increased_value = "", - .decreased_value = "", - .child_count = 0, - .children_in_traversal_order = nullptr, - .custom_accessibility_actions_count = 0, - .tooltip = "" - }; - accessibility_bridge_->AddFlutterSemanticsNodeUpdate(&announcement); - accessibility_bridge_->CommitUpdates(); - auto root = std::static_pointer_cast(accessibility_bridge_->GetFlutterPlatformNodeDelegateFromID(AccessibilityBridge::kRootNodeId).lock()); - FML_LOG(ERROR) << "Got root? " << (!!root); - auto root_node = root->GetAXNode(); - int32_t root_id = root_node->id(); - FML_LOG(ERROR) << "Root id = " << root_id; - for (auto it = root_node->UnignoredChildrenBegin(); it != root_node->UnignoredChildrenEnd(); ++it) { - FML_LOG(ERROR) << "ID of child: " << it->id(); - } - auto owner_tree = root_node->tree(); - auto siblings = root_node->children(); - int32_t index_in_parent = siblings.size(); - int32_t uindex = root_node->GetUnignoredChildCount(); - ui::AXNode* node = new ui::AXNode(owner_tree, root_node, -1, index_in_parent, uindex); - siblings.push_back(node); - root_node->SwapChildren(&siblings);*/ std::wstring wide_text = fml::Utf8ToWideString(text); view_->AnnounceAlert(wide_text); } diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 8284e2dc08d73..bcdcef41ba353 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -222,7 +222,6 @@ LRESULT Window::OnGetObject(UINT const message, } instance->AddRef(); accessibility_root_ = instance; - FML_LOG(ERROR) << "Creating accessibility root"; } // Return the IAccessible for the root view. //Microsoft::WRL::ComPtr root(root_view); @@ -614,7 +613,6 @@ void Window::Destroy() { } if (accessibility_root_) { - FML_LOG(ERROR) << "Destroying accessibility_root_!!\n"; accessibility_root_->Release(); accessibility_root_ = nullptr; } From 1873b2d136c29acf241b5edaac8f448829c4c80c Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 24 Oct 2022 11:37:43 -0400 Subject: [PATCH 07/21] Unit test alert node --- shell/platform/windows/flutter_window.cc | 3 ++ .../windows/flutter_window_unittests.cc | 28 +++++++++++++++++++ .../platform/windows/flutter_windows_view.cc | 4 ++- shell/platform/windows/window.cc | 20 +++++++------ shell/platform/windows/window.h | 3 ++ 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 1a335a77a05fc..c8d3e6a23e673 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -292,6 +292,9 @@ void FlutterWindow::SendInitialAccessibilityFeatures() { } AccessibilityRootNode* FlutterWindow::GetAccessibilityRootNode() { + if (!accessibility_root_) { + CreateAccessibilityRootNode(); + } return accessibility_root_; } diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index 9044c601c3685..af69c3bf95cc3 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -136,6 +136,7 @@ class MockFlutterWindow : public FlutterWindow { MOCK_METHOD3(Win32DispatchMessage, UINT(UINT, WPARAM, LPARAM)); MOCK_METHOD4(Win32PeekMessage, BOOL(LPMSG, UINT, UINT, UINT)); MOCK_METHOD1(Win32MapVkToChar, uint32_t(uint32_t)); + MOCK_METHOD0(GetPlatformWindow, HWND()); protected: // |KeyboardManager::WindowDelegate| @@ -400,5 +401,32 @@ TEST(FlutterWindowTest, InitialAccessibilityFeatures) { win32window.SendInitialAccessibilityFeatures(); } +// Ensure that announcing the alert propagates the message to the alert node. +// Different screen readers use different properties for alerts. +TEST(FlutterWindowTest, AlertNode) { + std::unique_ptr win32window = std::make_unique();; + ON_CALL(*win32window, GetPlatformWindow()).WillByDefault(Return(nullptr)); + AccessibilityRootNode* root_node = win32window->GetAccessibilityRootNode(); + TestFlutterWindowsView view(std::move(win32window)); + std::wstring message = L"Test alert"; + view.AnnounceAlert(message); + IAccessible* alert = root_node->GetOrCreateAlert(); + VARIANT self{.vt=VT_I4, .lVal=CHILDID_SELF}; + BSTR strptr; + alert->get_accName(self, &strptr); + EXPECT_EQ(message, strptr); + + alert->get_accDescription(self, &strptr); + EXPECT_EQ(message, strptr); + + alert->get_accValue(self, &strptr); + EXPECT_EQ(message, strptr); + + VARIANT role; + alert->get_accRole(self, &role); + EXPECT_EQ(role.vt, VT_I4); + EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 43ffea8448fc0..568324d96dc4b 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -660,7 +660,9 @@ void FlutterWindowsView::AnnounceAlert(const std::wstring& text) { AccessibilityAlert* alert = binding_handler_->GetAccessibilityRootNode()->GetOrCreateAlert(); alert->SetText(text); HWND hwnd = GetPlatformWindow(); - NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT, AccessibilityRootNode::kAlertChildId); + if (hwnd) { + NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT, AccessibilityRootNode::kAlertChildId); + } } } // namespace flutter diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index bcdcef41ba353..466b8ea01ebcb 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -214,14 +214,7 @@ LRESULT Window::OnGetObject(UINT const message, } else if (is_msaa_request && root_view) { // Create the accessibility root if it does not already exist. if (!accessibility_root_) { - ui::win::CreateATLModuleIfNeeded(); - CComObject* instance = nullptr; - HRESULT hr = CComObject::CreateInstance(&instance); - if (!SUCCEEDED(hr)) { - FML_LOG(FATAL) << "Failed to create accessibility root node"; - } - instance->AddRef(); - accessibility_root_ = instance; + CreateAccessibilityRootNode(); } // Return the IAccessible for the root view. //Microsoft::WRL::ComPtr root(root_view); @@ -669,4 +662,15 @@ bool Window::GetHighContrastEnabled() { } } +void Window::CreateAccessibilityRootNode() { + ui::win::CreateATLModuleIfNeeded(); + CComObject* instance = nullptr; + HRESULT hr = CComObject::CreateInstance(&instance); + if (!SUCCEEDED(hr)) { + FML_LOG(FATAL) << "Failed to create accessibility root node"; + } + instance->AddRef(); + accessibility_root_ = instance; +} + } // namespace flutter diff --git a/shell/platform/windows/window.h b/shell/platform/windows/window.h index fed064f347ee5..95784d0eff986 100644 --- a/shell/platform/windows/window.h +++ b/shell/platform/windows/window.h @@ -224,6 +224,9 @@ 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_; From f0a381092287b6c5b9640a4751dd72aad7994275 Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 24 Oct 2022 12:03:10 -0400 Subject: [PATCH 08/21] Linux tests --- ci/licenses_golden/licenses_flutter | 4 ++ shell/platform/windows/accessibility_alert.cc | 39 ++++++++++--------- shell/platform/windows/accessibility_alert.h | 3 +- .../windows/accessibility_root_node.cc | 32 +++++++++------ .../windows/accessibility_root_node.h | 3 +- .../windows/flutter_window_unittests.cc | 6 ++- .../windows/flutter_windows_engine.cc | 25 ++++++++---- .../platform/windows/flutter_windows_engine.h | 3 +- .../platform/windows/flutter_windows_view.cc | 6 ++- shell/platform/windows/window.cc | 2 +- 10 files changed, 75 insertions(+), 48 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c225182016aa6..1d4d1becba56f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -3059,9 +3059,13 @@ 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_delegate_windows.cc FILE: ../../../flutter/shell/platform/windows/accessibility_bridge_delegate_windows.h FILE: ../../../flutter/shell/platform/windows/accessibility_bridge_delegate_windows_unittests.cc +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/dart_project_unittests.cc diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc index 26e7741dd9659..cd54d4c9b5672 100644 --- a/shell/platform/windows/accessibility_alert.cc +++ b/shell/platform/windows/accessibility_alert.cc @@ -11,8 +11,8 @@ namespace flutter { AccessibilityAlert::AccessibilityAlert() : text_(L""), parent_(nullptr) {} IFACEMETHODIMP AccessibilityAlert::accHitTest(LONG screen_physical_pixel_x, - LONG screen_physical_pixel_y, - VARIANT* child) { + LONG screen_physical_pixel_y, + VARIANT* child) { child->vt = VT_EMPTY; return S_FALSE; } @@ -24,24 +24,24 @@ IFACEMETHODIMP AccessibilityAlert::accDoDefaultAction(VARIANT var_id) { // Retrieves the specified object's current screen location. IFACEMETHODIMP AccessibilityAlert::accLocation(LONG* physical_pixel_left, - LONG* physical_pixel_top, - LONG* width, - LONG* height, - VARIANT var_id) { + LONG* physical_pixel_top, + LONG* width, + LONG* height, + VARIANT var_id) { return E_NOTIMPL; } // Traverses to another UI element and retrieves the object. IFACEMETHODIMP AccessibilityAlert::accNavigate(LONG nav_dir, - VARIANT start, - VARIANT* end) { + VARIANT start, + VARIANT* end) { end->vt = VT_EMPTY; return E_NOTIMPL; } // Retrieves an IDispatch interface pointer for the specified child. IFACEMETHODIMP AccessibilityAlert::get_accChild(VARIANT var_child, - IDispatch** disp_child) { + IDispatch** disp_child) { if (V_VT(&var_child) == VT_I4 && V_I4(&var_child) == CHILDID_SELF) { *disp_child = this; AddRef(); @@ -59,13 +59,14 @@ IFACEMETHODIMP AccessibilityAlert::get_accChildCount(LONG* child_count) { // Retrieves a string that describes the object's default action. IFACEMETHODIMP AccessibilityAlert::get_accDefaultAction(VARIANT var_id, - BSTR* default_action) { + BSTR* default_action) { *default_action = nullptr; return E_NOTIMPL; } // Retrieves the tooltip description. -IFACEMETHODIMP AccessibilityAlert::get_accDescription(VARIANT var_id, BSTR* desc) { +IFACEMETHODIMP AccessibilityAlert::get_accDescription(VARIANT var_id, + BSTR* desc) { *desc = SysAllocString(text_.c_str()); return S_OK; } @@ -78,7 +79,7 @@ IFACEMETHODIMP AccessibilityAlert::get_accFocus(VARIANT* focus_child) { // Retrieves the specified object's shortcut. IFACEMETHODIMP AccessibilityAlert::get_accKeyboardShortcut(VARIANT var_id, - BSTR* access_key) { + BSTR* access_key) { *access_key = nullptr; return E_NOTIMPL; } @@ -97,7 +98,7 @@ IFACEMETHODIMP AccessibilityAlert::get_accParent(IDispatch** disp_parent) { } return S_OK; //*disp_parent = nullptr; - //return E_NOTIMPL; + // return E_NOTIMPL; } // Retrieves information describing the role of the specified object. @@ -107,7 +108,8 @@ IFACEMETHODIMP AccessibilityAlert::get_accRole(VARIANT var_id, VARIANT* role) { } // Retrieves the current state of the specified object. -IFACEMETHODIMP AccessibilityAlert::get_accState(VARIANT var_id, VARIANT* state) { +IFACEMETHODIMP AccessibilityAlert::get_accState(VARIANT var_id, + VARIANT* state) { *state = {.vt = VT_I4, .lVal = STATE_SYSTEM_DEFAULT}; return S_OK; } @@ -125,7 +127,8 @@ IFACEMETHODIMP AccessibilityAlert::get_accValue(VARIANT var_id, BSTR* value) { *value = SysAllocString(text_.c_str()); return S_OK; } -IFACEMETHODIMP AccessibilityAlert::put_accValue(VARIANT var_id, BSTR new_value) { +IFACEMETHODIMP AccessibilityAlert::put_accValue(VARIANT var_id, + BSTR new_value) { return E_NOTIMPL; } @@ -138,8 +141,8 @@ IFACEMETHODIMP AccessibilityAlert::accSelect(LONG flags_sel, VARIANT var_id) { return E_NOTIMPL; } IFACEMETHODIMP AccessibilityAlert::get_accHelpTopic(BSTR* help_file, - VARIANT var_id, - LONG* topic_id) { + VARIANT var_id, + LONG* topic_id) { if (help_file) { *help_file = nullptr; } @@ -160,4 +163,4 @@ 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 index a8fada247ff29..2c2a71e5e1d4d 100644 --- a/shell/platform/windows/accessibility_alert.h +++ b/shell/platform/windows/accessibility_alert.h @@ -108,9 +108,8 @@ class AccessibilityAlert : public CComObjectRootEx, std::wstring text_; AccessibilityRootNode* parent_; - }; -} +} // namespace flutter #endif diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index 47a8ea0db9f45..4bb307241acab 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -11,8 +11,7 @@ namespace flutter { static constexpr LONG kWindowChildId = 1; static constexpr LONG kInvalidChildId = 3; -AccessibilityRootNode::AccessibilityRootNode() : - alert_accessible_(nullptr) {} +AccessibilityRootNode::AccessibilityRootNode() : alert_accessible_(nullptr) {} AccessibilityRootNode::~AccessibilityRootNode() { if (alert_accessible_) { @@ -49,7 +48,8 @@ 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); + return window_accessible_->accHitTest(screen_physical_pixel_x, + screen_physical_pixel_y, child); } child->vt = VT_EMPTY; return S_FALSE; @@ -70,7 +70,8 @@ IFACEMETHODIMP AccessibilityRootNode::accLocation(LONG* physical_pixel_left, VARIANT var_id) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return target->accLocation(physical_pixel_left, physical_pixel_top, width, height, var_id); + return target->accLocation(physical_pixel_left, physical_pixel_top, width, + height, var_id); } return S_FALSE; } @@ -156,7 +157,8 @@ IFACEMETHODIMP AccessibilityRootNode::get_accKeyboardShortcut(VARIANT var_id, return E_FAIL; } -IFACEMETHODIMP AccessibilityRootNode::get_accName(VARIANT var_id, BSTR* name_bstr) { +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()); @@ -173,7 +175,8 @@ IFACEMETHODIMP AccessibilityRootNode::get_accParent(IDispatch** disp_parent) { return S_FALSE; } -IFACEMETHODIMP AccessibilityRootNode::get_accRole(VARIANT var_id, VARIANT* role) { +IFACEMETHODIMP AccessibilityRootNode::get_accRole(VARIANT var_id, + VARIANT* role) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { return target->get_accRole(var_id, role); @@ -181,7 +184,8 @@ IFACEMETHODIMP AccessibilityRootNode::get_accRole(VARIANT var_id, VARIANT* role) return E_FAIL; } -IFACEMETHODIMP AccessibilityRootNode::get_accState(VARIANT var_id, VARIANT* state) { +IFACEMETHODIMP AccessibilityRootNode::get_accState(VARIANT var_id, + VARIANT* state) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { return target->get_accState(var_id, state); @@ -197,7 +201,8 @@ IFACEMETHODIMP AccessibilityRootNode::get_accHelp(VARIANT var_id, BSTR* help) { return S_FALSE; } -IFACEMETHODIMP AccessibilityRootNode::get_accValue(VARIANT var_id, BSTR* value) { +IFACEMETHODIMP AccessibilityRootNode::get_accValue(VARIANT var_id, + BSTR* value) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { return target->get_accValue(var_id, value); @@ -205,7 +210,8 @@ IFACEMETHODIMP AccessibilityRootNode::get_accValue(VARIANT var_id, BSTR* value) return E_FAIL; } -IFACEMETHODIMP AccessibilityRootNode::put_accValue(VARIANT var_id, BSTR new_value) { +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); @@ -218,7 +224,8 @@ IFACEMETHODIMP AccessibilityRootNode::get_accSelection(VARIANT* selected) { return S_OK; } -IFACEMETHODIMP AccessibilityRootNode::accSelect(LONG flagsSelect, VARIANT var_id) { +IFACEMETHODIMP AccessibilityRootNode::accSelect(LONG flagsSelect, + VARIANT var_id) { IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { return target->accSelect(flagsSelect, var_id); @@ -238,7 +245,8 @@ IFACEMETHODIMP AccessibilityRootNode::get_accHelpTopic(BSTR* help_file, return E_NOTIMPL; } -IFACEMETHODIMP AccessibilityRootNode::put_accName(VARIANT var_id, BSTR put_name) { +IFACEMETHODIMP AccessibilityRootNode::put_accName(VARIANT var_id, + BSTR put_name) { return E_NOTIMPL; } @@ -260,4 +268,4 @@ AccessibilityAlert* AccessibilityRootNode::GetOrCreateAlert() { return alert_accessible_; } -} +} // namespace flutter diff --git a/shell/platform/windows/accessibility_root_node.h b/shell/platform/windows/accessibility_root_node.h index 3978e052efece..27e276da9692e 100644 --- a/shell/platform/windows/accessibility_root_node.h +++ b/shell/platform/windows/accessibility_root_node.h @@ -114,9 +114,8 @@ class AccessibilityRootNode : public CComObjectRootEx, IAccessible* window_accessible_; AccessibilityAlert* alert_accessible_; - }; -} +} // namespace flutter #endif diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index af69c3bf95cc3..83426331f6a12 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -404,14 +404,16 @@ TEST(FlutterWindowTest, InitialAccessibilityFeatures) { // Ensure that announcing the alert propagates the message to the alert node. // Different screen readers use different properties for alerts. TEST(FlutterWindowTest, AlertNode) { - std::unique_ptr win32window = std::make_unique();; + std::unique_ptr win32window = + std::make_unique(); + ; ON_CALL(*win32window, GetPlatformWindow()).WillByDefault(Return(nullptr)); AccessibilityRootNode* root_node = win32window->GetAccessibilityRootNode(); TestFlutterWindowsView view(std::move(win32window)); std::wstring message = L"Test alert"; view.AnnounceAlert(message); IAccessible* alert = root_node->GetOrCreateAlert(); - VARIANT self{.vt=VT_I4, .lVal=CHILDID_SELF}; + VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF}; BSTR strptr; alert->get_accName(self, &strptr); EXPECT_EQ(message, strptr); diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index a3281622c8295..887a50145af5a 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -187,10 +187,14 @@ FlutterWindowsEngine::FlutterWindowsEngine( messenger_wrapper_ = std::make_unique(messenger_.get()); message_dispatcher_ = std::make_unique(messenger_.get()); - message_dispatcher_->SetMessageCallback(kAccessibilityChannelName, [](FlutterDesktopMessengerRef messenger, const FlutterDesktopMessage* message, void* data){ - FlutterWindowsEngine* engine = static_cast(data); - engine->HandleAccessibilityMessage(messenger, message); - }, static_cast(this)); + message_dispatcher_->SetMessageCallback( + kAccessibilityChannelName, + [](FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessage* message, void* data) { + FlutterWindowsEngine* engine = static_cast(data); + engine->HandleAccessibilityMessage(messenger, message); + }, + static_cast(this)); FlutterWindowsTextureRegistrar::ResolveGlFunctions(gl_procs_); texture_registrar_ = @@ -668,20 +672,25 @@ int FlutterWindowsEngine::EnabledAccessibilityFeatures() const { return flags; } -void FlutterWindowsEngine::HandleAccessibilityMessage(FlutterDesktopMessengerRef messenger, const FlutterDesktopMessage* message) { +void FlutterWindowsEngine::HandleAccessibilityMessage( + FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessage* message) { const auto& codec = StandardMessageCodec::GetInstance(); auto data = codec.DecodeMessage(message->message, message->message_size); EncodableMap map = std::get(*data); std::string type = std::get(map.at(EncodableValue("type"))); if (type.compare("announce") == 0) { if (semantics_enabled_) { - EncodableMap data_map = std::get(map.at(EncodableValue("data"))); - std::string text = std::get(data_map.at(EncodableValue("message"))); + EncodableMap data_map = + std::get(map.at(EncodableValue("data"))); + std::string text = + std::get(data_map.at(EncodableValue("message"))); std::wstring wide_text = fml::Utf8ToWideString(text); view_->AnnounceAlert(wide_text); } } - SendPlatformMessageResponse(message->response_handle, reinterpret_cast(""), 0); + SendPlatformMessageResponse(message->response_handle, + reinterpret_cast(""), 0); } } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 09add7386a96f..3cdee97e3cb92 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -254,7 +254,8 @@ class FlutterWindowsEngine { // system changes. void SendSystemLocales(); - void HandleAccessibilityMessage(FlutterDesktopMessengerRef messenger, const FlutterDesktopMessage* message); + void HandleAccessibilityMessage(FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessage* message); // The handle to the embedder.h engine instance. FLUTTER_API_SYMBOL(FlutterEngine) engine_ = nullptr; diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 568324d96dc4b..c3f23208a18c9 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -657,11 +657,13 @@ FlutterWindowsEngine* FlutterWindowsView::GetEngine() { } void FlutterWindowsView::AnnounceAlert(const std::wstring& text) { - AccessibilityAlert* alert = binding_handler_->GetAccessibilityRootNode()->GetOrCreateAlert(); + AccessibilityAlert* alert = + binding_handler_->GetAccessibilityRootNode()->GetOrCreateAlert(); alert->SetText(text); HWND hwnd = GetPlatformWindow(); if (hwnd) { - NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT, AccessibilityRootNode::kAlertChildId); + NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT, + AccessibilityRootNode::kAlertChildId); } } diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 466b8ea01ebcb..154d3fc584e9c 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -217,7 +217,7 @@ LRESULT Window::OnGetObject(UINT const message, CreateAccessibilityRootNode(); } // Return the IAccessible for the root view. - //Microsoft::WRL::ComPtr root(root_view); + // Microsoft::WRL::ComPtr root(root_view); accessibility_root_->SetWindow(root_view); Microsoft::WRL::ComPtr root(accessibility_root_); // TODO(schectman): wrap returned accessible in AccessibilityRootNode From 8ab628ab66ea47a50966e869dd21aa642e6054d8 Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 24 Oct 2022 14:55:37 -0400 Subject: [PATCH 09/21] PR --- shell/platform/windows/accessibility_alert.cc | 8 +++-- shell/platform/windows/accessibility_alert.h | 32 ++++++++----------- .../windows/accessibility_root_node.cc | 3 +- shell/platform/windows/window.cc | 2 +- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc index cd54d4c9b5672..89be38f83d715 100644 --- a/shell/platform/windows/accessibility_alert.cc +++ b/shell/platform/windows/accessibility_alert.cc @@ -95,10 +95,9 @@ IFACEMETHODIMP AccessibilityAlert::get_accParent(IDispatch** disp_parent) { *disp_parent = parent_; if (*disp_parent) { (*disp_parent)->AddRef(); + return S_OK; } - return S_OK; - //*disp_parent = nullptr; - // return E_NOTIMPL; + return S_FALSE; } // Retrieves information describing the role of the specified object. @@ -127,6 +126,7 @@ IFACEMETHODIMP AccessibilityAlert::get_accValue(VARIANT var_id, BSTR* value) { *value = SysAllocString(text_.c_str()); return S_OK; } + IFACEMETHODIMP AccessibilityAlert::put_accValue(VARIANT var_id, BSTR new_value) { return E_NOTIMPL; @@ -137,9 +137,11 @@ 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::get_accHelpTopic(BSTR* help_file, VARIANT var_id, LONG* topic_id) { diff --git a/shell/platform/windows/accessibility_alert.h b/shell/platform/windows/accessibility_alert.h index 2c2a71e5e1d4d..e15f5f611f795 100644 --- a/shell/platform/windows/accessibility_alert.h +++ b/shell/platform/windows/accessibility_alert.h @@ -32,19 +32,6 @@ class AccessibilityAlert : public CComObjectRootEx, 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, @@ -61,11 +48,6 @@ class AccessibilityAlert : public CComObjectRootEx, 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; @@ -86,15 +68,27 @@ class AccessibilityAlert : public CComObjectRootEx, // 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 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; AccessibilityAlert(); ~AccessibilityAlert() = default; diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index 4bb307241acab..17bd3e759e3bf 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -24,7 +24,7 @@ IAccessible* AccessibilityRootNode::GetTargetAndChildID(VARIANT* var_id) { LONG& child_id = var_id->lVal; if (V_VT(var_id) != VT_I4) { child_id = kInvalidChildId; - return FALSE; + return nullptr; } child_id = V_I4(var_id); if (!window_accessible_) { @@ -38,6 +38,7 @@ IAccessible* AccessibilityRootNode::GetTargetAndChildID(VARIANT* var_id) { 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_; } diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 154d3fc584e9c..048591da33dac 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -666,7 +666,7 @@ void Window::CreateAccessibilityRootNode() { ui::win::CreateATLModuleIfNeeded(); CComObject* instance = nullptr; HRESULT hr = CComObject::CreateInstance(&instance); - if (!SUCCEEDED(hr)) { + if (!SUCCEEDED(hr) || !instance) { FML_LOG(FATAL) << "Failed to create accessibility root node"; } instance->AddRef(); From 62e4ecc2eac20485b370fa0f786578446947a56f Mon Sep 17 00:00:00 2001 From: yaakovschectman <109111084+yaakovschectman@users.noreply.github.com> Date: Mon, 24 Oct 2022 15:26:25 -0400 Subject: [PATCH 10/21] Update shell/platform/windows/accessibility_root_node.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com> --- shell/platform/windows/accessibility_root_node.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/windows/accessibility_root_node.h b/shell/platform/windows/accessibility_root_node.h index 27e276da9692e..f421485ff5b8b 100644 --- a/shell/platform/windows/accessibility_root_node.h +++ b/shell/platform/windows/accessibility_root_node.h @@ -108,7 +108,7 @@ class AccessibilityRootNode : public CComObjectRootEx, AccessibilityAlert* GetOrCreateAlert(); private: - // Helper method to redirect method calls to the contained window. + // Helper method to redirect method calls to the contained window or alert. IAccessible* GetTargetAndChildID(VARIANT* var_id); IAccessible* window_accessible_; From dc219b21f07249cfede943206d599856c7e59d20 Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 24 Oct 2022 16:28:46 -0400 Subject: [PATCH 11/21] Add comment --- shell/platform/windows/accessibility_root_node.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index 17bd3e759e3bf..1977cda6b0988 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -102,6 +102,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accChild(VARIANT var_child, } 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; From 0c43076a3884504b712dd156bb8d362e071e4222 Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 25 Oct 2022 10:37:31 -0400 Subject: [PATCH 12/21] Expect call to wrapper --- shell/platform/windows/flutter_window_unittests.cc | 5 ++++- shell/platform/windows/flutter_windows_view.cc | 4 ++++ shell/platform/windows/flutter_windows_view.h | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index 83426331f6a12..1a1cd32168a83 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -154,10 +154,13 @@ class TestFlutterWindowsView : public FlutterWindowsView { public: TestFlutterWindowsView(std::unique_ptr window_binding) : FlutterWindowsView(std::move(window_binding)) {} + ~TestFlutterWindowsView() {} SpyKeyboardKeyHandler* key_event_handler; SpyTextInputPlugin* text_input_plugin; + MOCK_METHOD4(NotifyWinEventWrapper, void(DWORD, HWND, LONG, LONG)); + protected: std::unique_ptr CreateKeyboardKeyHandler( flutter::BinaryMessenger* messenger, @@ -406,10 +409,10 @@ TEST(FlutterWindowTest, InitialAccessibilityFeatures) { TEST(FlutterWindowTest, AlertNode) { std::unique_ptr win32window = std::make_unique(); - ; ON_CALL(*win32window, GetPlatformWindow()).WillByDefault(Return(nullptr)); AccessibilityRootNode* root_node = win32window->GetAccessibilityRootNode(); 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"; view.AnnounceAlert(message); IAccessible* alert = root_node->GetOrCreateAlert(); diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index c3f23208a18c9..ccca8b78b2cb4 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -661,6 +661,10 @@ void FlutterWindowsView::AnnounceAlert(const std::wstring& text) { 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); diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 4918b4db3fa5e..9dc4317244225 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -214,6 +214,8 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, virtual std::unique_ptr CreateTextInputPlugin( BinaryMessenger* messenger); + virtual void NotifyWinEventWrapper(DWORD event, HWND hwnd, LONG idObject, LONG idChild); + private: // Struct holding the state of an individual pointer. The engine doesn't keep // track of which buttons have been pressed, so it's the embedding's From c0d5a17858bd4cff6197d72a39f7775c44faeaf4 Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 25 Oct 2022 12:12:04 -0400 Subject: [PATCH 13/21] Unit test announcement message --- .../windows/accessibility_root_node.cc | 13 +++++ .../windows/accessibility_root_node.h | 4 +- shell/platform/windows/fixtures/main.dart | 43 ++++++++++++++- .../flutter_windows_engine_unittests.cc | 55 +++++++++++++++++++ .../platform/windows/flutter_windows_view.cc | 1 + shell/platform/windows/window.cc | 10 +--- 6 files changed, 115 insertions(+), 11 deletions(-) diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index 1977cda6b0988..7a3f32678f048 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -5,6 +5,7 @@ #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 { @@ -270,4 +271,16 @@ AccessibilityAlert* AccessibilityRootNode::GetOrCreateAlert() { 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 index f421485ff5b8b..1b1bdf4d6873c 100644 --- a/shell/platform/windows/accessibility_root_node.h +++ b/shell/platform/windows/accessibility_root_node.h @@ -99,7 +99,7 @@ class AccessibilityRootNode : public CComObjectRootEx, IFACEMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override; AccessibilityRootNode(); - ~AccessibilityRootNode(); + virtual ~AccessibilityRootNode(); void SetWindow(IAccessible* window); @@ -107,6 +107,8 @@ class AccessibilityRootNode : public CComObjectRootEx, AccessibilityAlert* GetOrCreateAlert(); + static AccessibilityRootNode* Create(); + private: // Helper method to redirect method calls to the contained window or alert. IAccessible* GetTargetAndChildID(VARIANT* var_id); diff --git a/shell/platform/windows/fixtures/main.dart b/shell/platform/windows/fixtures/main.dart index 43de4bea59496..eb6f0ad585ddd 100644 --- a/shell/platform/windows/fixtures/main.dart +++ b/shell/platform/windows/fixtures/main.dart @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:io' as io; -import 'dart:typed_data' show ByteData; +import 'dart:typed_data' show ByteData, Uint8List; import 'dart:ui' as ui; // Signals a waiting latch in the native test. @@ -36,6 +37,46 @@ void hiPlatformChannels() { }); } +@pragma('vm:entry-point') +void alertPlatformChannel() async { + // Serializers for data types are in the framework, so this will be hardcoded. + Uint8List data = Uint8List.fromList([ + 13, // _valueMap + 2, // Size + // key: "type" + 7, // _valueString + 'type'.length, + ...'type'.codeUnits, + // value: "announce" + 7, + 'announce'.length, + ...'announce'.codeUnits, + // key: "data" + 7, // _valueString + 'data'.length, + ...'data'.codeUnits, + // value: map + 13, // _valueMap + 1, // Size + // key: "message" + 7, // _valueString + 'message'.length, + ...'message'.codeUnits, + // value: "" + 7, // _valueString + 0 + ]); + ByteData byteData = data.buffer.asByteData(); + + final Completer enabled = Completer(); + ui.PlatformDispatcher.instance.sendPlatformMessage('semantics', ByteData(0), (ByteData? reply){ + enabled.complete(reply); + }); + await enabled.future; + + ui.PlatformDispatcher.instance.sendPlatformMessage('flutter/accessibility', byteData, (ByteData? _){}); +} + @pragma('vm:entry-point') void customEntrypoint() {} diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 4b44a0919f9ad..074517468d62e 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -6,10 +6,13 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" +#include "flutter/shell/platform/windows/flutter_windows_view.h" #include "flutter/shell/platform/windows/testing/engine_modifier.h" +#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 "fml/synchronization/waitable_event.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" // winbase.h defines GetCurrentTime as a macro. @@ -516,5 +519,57 @@ TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) { EXPECT_TRUE(called); } +class MockFlutterWindowsView : public FlutterWindowsView { + public: + MockFlutterWindowsView(std::unique_ptr wbh) : FlutterWindowsView(std::move(wbh)) {} + ~MockFlutterWindowsView() {} + + MOCK_METHOD4(NotifyWinEventWrapper, void(DWORD, HWND, LONG, LONG)); +}; + +TEST_F(FlutterWindowsEngineTest, AlertPlatformMessage) { + FlutterDesktopEngineProperties properties = {}; + properties.assets_path = GetContext().GetAssetsPath().c_str(); + properties.icu_data_path = GetContext().GetIcuDataPath().c_str(); + properties.dart_entrypoint = "alertPlatformChannel"; + + FlutterProjectBundle project(properties); + + 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)); + MockFlutterWindowsView view(std::move(window_binding_handler)); + view.SetEngine(std::make_unique(project)); + FlutterWindowsEngine* engine = view.GetEngine(); + + EngineModifier modifier(engine); + modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; }; + + auto binary_messenger = + std::make_unique(engine->messenger()); + binary_messenger->SetMessageHandler( + "semantics", + [&engine]( + const uint8_t* message, size_t message_size, BinaryReply reply) { + engine->UpdateSemanticsEnabled(true); + char response[] = ""; + reply(reinterpret_cast(response), 0); + }); + + bool did_call = false; + ON_CALL(view, NotifyWinEventWrapper).WillByDefault([&did_call](DWORD event, HWND hwnd, LONG obj, LONG child) { + did_call = true; + }); + + + engine->UpdateSemanticsEnabled(true); + engine->Run(); + + // Rely on timeout mechanism in CI. + while (!did_call) { + engine->task_runner()->ProcessTasks(); + } +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index ccca8b78b2cb4..99254792bd542 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -657,6 +657,7 @@ 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); diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 048591da33dac..0dd95c5b78879 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -15,7 +15,6 @@ #include #include "flutter/shell/platform/windows/dpi_utils.h" -#include "flutter/third_party/accessibility/base/win/atl_module.h" namespace flutter { @@ -663,14 +662,7 @@ bool Window::GetHighContrastEnabled() { } void Window::CreateAccessibilityRootNode() { - 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(); - accessibility_root_ = instance; + accessibility_root_ = AccessibilityRootNode::Create(); } } // namespace flutter From 885f4dc4c54fdbe982fd15699becd50816635ebd Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 25 Oct 2022 13:04:17 -0400 Subject: [PATCH 14/21] Format --- .../windows/accessibility_root_node.cc | 3 +- .../windows/flutter_window_unittests.cc | 5 +++- .../flutter_windows_engine_unittests.cc | 30 ++++++++++--------- .../platform/windows/flutter_windows_view.cc | 11 +++++-- shell/platform/windows/flutter_windows_view.h | 5 +++- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index 7a3f32678f048..15d422b6594fb 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -103,7 +103,8 @@ IFACEMETHODIMP AccessibilityRootNode::get_accChild(VARIANT var_child, } 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. + // 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; diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index 1a1cd32168a83..2d149b6294367 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -412,7 +412,10 @@ TEST(FlutterWindowTest, AlertNode) { ON_CALL(*win32window, GetPlatformWindow()).WillByDefault(Return(nullptr)); AccessibilityRootNode* root_node = win32window->GetAccessibilityRootNode(); TestFlutterWindowsView view(std::move(win32window)); - EXPECT_CALL(view, NotifyWinEventWrapper(EVENT_SYSTEM_ALERT, nullptr, OBJID_CLIENT, AccessibilityRootNode::kAlertChildId)).Times(1); + EXPECT_CALL(view, + NotifyWinEventWrapper(EVENT_SYSTEM_ALERT, nullptr, OBJID_CLIENT, + AccessibilityRootNode::kAlertChildId)) + .Times(1); std::wstring message = L"Test alert"; view.AnnounceAlert(message); IAccessible* alert = root_node->GetOrCreateAlert(); diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 074517468d62e..a89322f0c51b9 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -521,7 +521,8 @@ TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) { class MockFlutterWindowsView : public FlutterWindowsView { public: - MockFlutterWindowsView(std::unique_ptr wbh) : FlutterWindowsView(std::move(wbh)) {} + MockFlutterWindowsView(std::unique_ptr wbh) + : FlutterWindowsView(std::move(wbh)) {} ~MockFlutterWindowsView() {} MOCK_METHOD4(NotifyWinEventWrapper, void(DWORD, HWND, LONG, LONG)); @@ -535,9 +536,11 @@ TEST_F(FlutterWindowsEngineTest, AlertPlatformMessage) { FlutterProjectBundle project(properties); - auto window_binding_handler = std::make_unique<::testing::NiceMock>(); + 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)); + ON_CALL(*window_binding_handler, GetAccessibilityRootNode) + .WillByDefault(::testing::Return(root_node)); MockFlutterWindowsView view(std::move(window_binding_handler)); view.SetEngine(std::make_unique(project)); FlutterWindowsEngine* engine = view.GetEngine(); @@ -548,23 +551,22 @@ TEST_F(FlutterWindowsEngineTest, AlertPlatformMessage) { auto binary_messenger = std::make_unique(engine->messenger()); binary_messenger->SetMessageHandler( - "semantics", - [&engine]( - const uint8_t* message, size_t message_size, BinaryReply reply) { - engine->UpdateSemanticsEnabled(true); - char response[] = ""; - reply(reinterpret_cast(response), 0); + "semantics", [&engine](const uint8_t* message, size_t message_size, + BinaryReply reply) { + engine->UpdateSemanticsEnabled(true); + char response[] = ""; + reply(reinterpret_cast(response), 0); }); bool did_call = false; - ON_CALL(view, NotifyWinEventWrapper).WillByDefault([&did_call](DWORD event, HWND hwnd, LONG obj, LONG child) { - did_call = true; - }); - + ON_CALL(view, NotifyWinEventWrapper) + .WillByDefault([&did_call](DWORD event, HWND hwnd, LONG obj, LONG child) { + did_call = true; + }); engine->UpdateSemanticsEnabled(true); engine->Run(); - + // Rely on timeout mechanism in CI. while (!did_call) { engine->task_runner()->ProcessTasks(); diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 99254792bd542..0f04f57bf7ee6 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -657,15 +657,20 @@ FlutterWindowsEngine* FlutterWindowsView::GetEngine() { } void FlutterWindowsView::AnnounceAlert(const std::wstring& text) { - AccessibilityRootNode* root_node = binding_handler_->GetAccessibilityRootNode(); + 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); + NotifyWinEventWrapper(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT, + AccessibilityRootNode::kAlertChildId); } -void FlutterWindowsView::NotifyWinEventWrapper(DWORD event, HWND hwnd, LONG idObject, LONG idChild) { +void FlutterWindowsView::NotifyWinEventWrapper(DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild) { if (hwnd) { NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_CLIENT, AccessibilityRootNode::kAlertChildId); diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 9dc4317244225..f2b38ed22e724 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -214,7 +214,10 @@ 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(DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild); private: // Struct holding the state of an individual pointer. The engine doesn't keep From 1ccb8472b6b3b9382fb06ebf01a3d83baf8c9edd Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 25 Oct 2022 14:34:34 -0400 Subject: [PATCH 15/21] Constants in fixture --- shell/platform/windows/fixtures/main.dart | 24 +++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/shell/platform/windows/fixtures/main.dart b/shell/platform/windows/fixtures/main.dart index eb6f0ad585ddd..fe84072e6908d 100644 --- a/shell/platform/windows/fixtures/main.dart +++ b/shell/platform/windows/fixtures/main.dart @@ -40,33 +40,37 @@ void hiPlatformChannels() { @pragma('vm:entry-point') void alertPlatformChannel() async { // Serializers for data types are in the framework, so this will be hardcoded. - Uint8List data = Uint8List.fromList([ - 13, // _valueMap + const int valueMap = 13, valueString = 7; + // Corresponds to: + // Map data = + // {"type": "announce", "data": {"message": ""}}; + final Uint8List data = Uint8List.fromList([ + valueMap, // _valueMap 2, // Size // key: "type" - 7, // _valueString + valueString, 'type'.length, ...'type'.codeUnits, // value: "announce" - 7, + valueString, 'announce'.length, ...'announce'.codeUnits, // key: "data" - 7, // _valueString + valueString, 'data'.length, ...'data'.codeUnits, // value: map - 13, // _valueMap + valueMap, // _valueMap 1, // Size // key: "message" - 7, // _valueString + valueString, 'message'.length, ...'message'.codeUnits, // value: "" - 7, // _valueString - 0 + valueString, + 0, // Length of empty string == 0. ]); - ByteData byteData = data.buffer.asByteData(); + final ByteData byteData = data.buffer.asByteData(); final Completer enabled = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage('semantics', ByteData(0), (ByteData? reply){ From da7a773e043c673146ca00bd7bccb8500b4c5ac5 Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 26 Oct 2022 14:40:15 -0400 Subject: [PATCH 16/21] PR --- shell/platform/windows/accessibility_alert.cc | 4 ++++ shell/platform/windows/accessibility_alert.h | 15 ++++++++------- shell/platform/windows/accessibility_root_node.cc | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc index 89be38f83d715..9ae4c56d8eed6 100644 --- a/shell/platform/windows/accessibility_alert.cc +++ b/shell/platform/windows/accessibility_alert.cc @@ -10,6 +10,8 @@ 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) { @@ -157,6 +159,8 @@ IFACEMETHODIMP AccessibilityAlert::put_accName(VARIANT var_id, BSTR put_name) { return E_NOTIMPL; } +// End of IAccessible methods. + void AccessibilityAlert::SetText(const std::wstring& text) { text_ = text; } diff --git a/shell/platform/windows/accessibility_alert.h b/shell/platform/windows/accessibility_alert.h index e15f5f611f795..10b348556f74a 100644 --- a/shell/platform/windows/accessibility_alert.h +++ b/shell/platform/windows/accessibility_alert.h @@ -16,6 +16,11 @@ 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 AccessibilityAlert : public CComObjectRootEx, public IDispatchImpl { public: @@ -31,8 +36,6 @@ class AccessibilityAlert : public CComObjectRootEx, LONG screen_physical_pixel_y, VARIANT* child) override; - // Performs the object's default action. - // Retrieves an IDispatch interface pointer for the specified child. IFACEMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child) override; @@ -47,8 +50,6 @@ class AccessibilityAlert : public CComObjectRootEx, // Retrieves the tooltip description. IFACEMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override; - // Retrieves the object that has the keyboard focus. - // Retrieves the name of the specified object. IFACEMETHODIMP get_accName(VARIANT var_id, BSTR* name) override; @@ -64,9 +65,7 @@ class AccessibilityAlert : public CComObjectRootEx, // 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. + // Retrieve the string value associated with the specified object. IFACEMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override; // IAccessible methods not implemented. @@ -90,6 +89,8 @@ class AccessibilityAlert : public CComObjectRootEx, IFACEMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override; IFACEMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override; + // End of IAccessible methods. + AccessibilityAlert(); ~AccessibilityAlert() = default; diff --git a/shell/platform/windows/accessibility_root_node.cc b/shell/platform/windows/accessibility_root_node.cc index 15d422b6594fb..350cf66f17b27 100644 --- a/shell/platform/windows/accessibility_root_node.cc +++ b/shell/platform/windows/accessibility_root_node.cc @@ -170,7 +170,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accName(VARIANT var_id, } IAccessible* target; if ((target = GetTargetAndChildID(&var_id))) { - return window_accessible_->get_accName(var_id, name_bstr); + return target->get_accName(var_id, name_bstr); } return S_FALSE; } From 957f9f542795465797ab71b1b41372bb274e56da Mon Sep 17 00:00:00 2001 From: yaakovschectman <109111084+yaakovschectman@users.noreply.github.com> Date: Wed, 26 Oct 2022 15:19:46 -0400 Subject: [PATCH 17/21] Update shell/platform/windows/accessibility_root_node.h Co-authored-by: Chris Bracken --- shell/platform/windows/accessibility_root_node.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/windows/accessibility_root_node.h b/shell/platform/windows/accessibility_root_node.h index 1b1bdf4d6873c..efe72d64f4721 100644 --- a/shell/platform/windows/accessibility_root_node.h +++ b/shell/platform/windows/accessibility_root_node.h @@ -120,4 +120,4 @@ class AccessibilityRootNode : public CComObjectRootEx, } // namespace flutter -#endif +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_ROOT_NODE_H_ From 3876e99e383764ccdea51caf08dff9ae71527fa9 Mon Sep 17 00:00:00 2001 From: yaakovschectman <109111084+yaakovschectman@users.noreply.github.com> Date: Wed, 26 Oct 2022 15:20:04 -0400 Subject: [PATCH 18/21] Update shell/platform/windows/accessibility_alert.h Co-authored-by: Chris Bracken --- shell/platform/windows/accessibility_alert.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/windows/accessibility_alert.h b/shell/platform/windows/accessibility_alert.h index 10b348556f74a..c60ede1479030 100644 --- a/shell/platform/windows/accessibility_alert.h +++ b/shell/platform/windows/accessibility_alert.h @@ -107,4 +107,4 @@ class AccessibilityAlert : public CComObjectRootEx, } // namespace flutter -#endif +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_ACCESSIBILITY_ALERT_H_ From de9da98acca2f3e852366e5209ac4f1f07b4a918 Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 26 Oct 2022 15:40:13 -0400 Subject: [PATCH 19/21] Release a11y node if non null --- shell/platform/windows/window.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 0dd95c5b78879..a1272f889cca7 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -662,6 +662,9 @@ bool Window::GetHighContrastEnabled() { } void Window::CreateAccessibilityRootNode() { + if (accessibility_root_) { + accessibility_root_->Release(); + } accessibility_root_ = AccessibilityRootNode::Create(); } From 9317a8842818c1612c515b010c1942493c5982ff Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 26 Oct 2022 17:02:55 -0400 Subject: [PATCH 20/21] Reorder methods --- shell/platform/windows/accessibility_alert.cc | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/shell/platform/windows/accessibility_alert.cc b/shell/platform/windows/accessibility_alert.cc index 9ae4c56d8eed6..9ac8cbda5fe3a 100644 --- a/shell/platform/windows/accessibility_alert.cc +++ b/shell/platform/windows/accessibility_alert.cc @@ -24,23 +24,6 @@ IFACEMETHODIMP AccessibilityAlert::accDoDefaultAction(VARIANT var_id) { return E_FAIL; } -// Retrieves the specified object's current screen location. -IFACEMETHODIMP AccessibilityAlert::accLocation(LONG* physical_pixel_left, - LONG* physical_pixel_top, - LONG* width, - LONG* height, - VARIANT var_id) { - return E_NOTIMPL; -} - -// Traverses to another UI element and retrieves the object. -IFACEMETHODIMP AccessibilityAlert::accNavigate(LONG nav_dir, - VARIANT start, - VARIANT* end) { - end->vt = VT_EMPTY; - return E_NOTIMPL; -} - // Retrieves an IDispatch interface pointer for the specified child. IFACEMETHODIMP AccessibilityAlert::get_accChild(VARIANT var_child, IDispatch** disp_child) { @@ -59,13 +42,6 @@ IFACEMETHODIMP AccessibilityAlert::get_accChildCount(LONG* child_count) { return S_OK; } -// Retrieves a string that describes the object's default action. -IFACEMETHODIMP AccessibilityAlert::get_accDefaultAction(VARIANT var_id, - BSTR* default_action) { - *default_action = nullptr; - return E_NOTIMPL; -} - // Retrieves the tooltip description. IFACEMETHODIMP AccessibilityAlert::get_accDescription(VARIANT var_id, BSTR* desc) { @@ -73,19 +49,6 @@ IFACEMETHODIMP AccessibilityAlert::get_accDescription(VARIANT var_id, return S_OK; } -// Retrieves the object that has the keyboard focus. -IFACEMETHODIMP AccessibilityAlert::get_accFocus(VARIANT* focus_child) { - focus_child->vt = VT_EMPTY; - return E_NOTIMPL; -} - -// Retrieves the specified object's shortcut. -IFACEMETHODIMP AccessibilityAlert::get_accKeyboardShortcut(VARIANT var_id, - BSTR* access_key) { - *access_key = nullptr; - return E_NOTIMPL; -} - // Retrieves the name of the specified object. IFACEMETHODIMP AccessibilityAlert::get_accName(VARIANT var_id, BSTR* name) { *name = SysAllocString(text_.c_str()); @@ -129,11 +92,6 @@ IFACEMETHODIMP AccessibilityAlert::get_accValue(VARIANT var_id, BSTR* value) { return S_OK; } -IFACEMETHODIMP AccessibilityAlert::put_accValue(VARIANT var_id, - BSTR new_value) { - return E_NOTIMPL; -} - // IAccessible methods not implemented. IFACEMETHODIMP AccessibilityAlert::get_accSelection(VARIANT* selected) { selected->vt = VT_EMPTY; @@ -144,6 +102,16 @@ 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) { @@ -155,10 +123,38 @@ IFACEMETHODIMP AccessibilityAlert::get_accHelpTopic(BSTR* help_file, } 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. void AccessibilityAlert::SetText(const std::wstring& text) { From 2dfd8ecce37b54d8dd6a8a2ecfa37d676e677933 Mon Sep 17 00:00:00 2001 From: schectman Date: Thu, 27 Oct 2022 13:35:53 -0400 Subject: [PATCH 21/21] Remove TODO --- shell/platform/windows/window.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index a1272f889cca7..9581d05ab9d51 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -219,7 +219,6 @@ LRESULT Window::OnGetObject(UINT const message, // Microsoft::WRL::ComPtr root(root_view); accessibility_root_->SetWindow(root_view); Microsoft::WRL::ComPtr root(accessibility_root_); - // TODO(schectman): wrap returned accessible in AccessibilityRootNode LRESULT lresult = LresultFromObject(IID_IAccessible, wparam, root.Get()); return lresult; }