Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Unit test announcement message
  • Loading branch information
yaakovschectman committed Oct 26, 2022
commit c0d5a17858bd4cff6197d72a39f7775c44faeaf4
13 changes: 13 additions & 0 deletions shell/platform/windows/accessibility_root_node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -270,4 +271,16 @@ AccessibilityAlert* AccessibilityRootNode::GetOrCreateAlert() {
return alert_accessible_;
}

// static
AccessibilityRootNode* AccessibilityRootNode::Create() {
ui::win::CreateATLModuleIfNeeded();
CComObject<AccessibilityRootNode>* instance = nullptr;
HRESULT hr = CComObject<AccessibilityRootNode>::CreateInstance(&instance);
if (!SUCCEEDED(hr) || !instance) {
FML_LOG(FATAL) << "Failed to create accessibility root node";
}
instance->AddRef();
return instance;
}

} // namespace flutter
4 changes: 3 additions & 1 deletion shell/platform/windows/accessibility_root_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,16 @@ class AccessibilityRootNode : public CComObjectRootEx<CComMultiThreadModel>,
IFACEMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override;

AccessibilityRootNode();
~AccessibilityRootNode();
virtual ~AccessibilityRootNode();

void SetWindow(IAccessible* window);

void SetAlert(AccessibilityAlert* alert);

AccessibilityAlert* GetOrCreateAlert();

static AccessibilityRootNode* Create();

private:
// Helper method to redirect method calls to the contained window or alert.
IAccessible* GetTargetAndChildID(VARIANT* var_id);
Expand Down
43 changes: 42 additions & 1 deletion shell/platform/windows/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<ByteData?> enabled = Completer<ByteData?>();
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() {}

Expand Down
55 changes: 55 additions & 0 deletions shell/platform/windows/flutter_windows_engine_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -516,5 +519,57 @@ TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) {
EXPECT_TRUE(called);
}

class MockFlutterWindowsView : public FlutterWindowsView {
public:
MockFlutterWindowsView(std::unique_ptr<WindowBindingHandler> 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<MockWindowBindingHandler>>();
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<FlutterWindowsEngine>(project));
FlutterWindowsEngine* engine = view.GetEngine();

EngineModifier modifier(engine);
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };

auto binary_messenger =
std::make_unique<BinaryMessengerImpl>(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<uint8_t*>(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
1 change: 1 addition & 0 deletions shell/platform/windows/flutter_windows_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 1 addition & 9 deletions shell/platform/windows/window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include <cstring>

#include "flutter/shell/platform/windows/dpi_utils.h"
#include "flutter/third_party/accessibility/base/win/atl_module.h"

namespace flutter {

Expand Down Expand Up @@ -663,14 +662,7 @@ bool Window::GetHighContrastEnabled() {
}

void Window::CreateAccessibilityRootNode() {
ui::win::CreateATLModuleIfNeeded();
CComObject<AccessibilityRootNode>* instance = nullptr;
HRESULT hr = CComObject<AccessibilityRootNode>::CreateInstance(&instance);
if (!SUCCEEDED(hr) || !instance) {
FML_LOG(FATAL) << "Failed to create accessibility root node";
}
instance->AddRef();
accessibility_root_ = instance;
accessibility_root_ = AccessibilityRootNode::Create();
}

} // namespace flutter