| Flutter Windows Embedder |

flutter_windows_view_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/shell/platform/windows/flutter_windows_view.h"
6
7 #include <UIAutomation.h>
8 #include <comdef.h>
9 #include <comutil.h>
10 #include <oleacc.h>
11
12 #include <future>
13 #include <vector>
14
15 #include "flutter/fml/synchronization/waitable_event.h"
16 #include "flutter/shell/platform/common/json_message_codec.h"
17 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
18 #include "flutter/shell/platform/windows/flutter_window.h"
19 #include "flutter/shell/platform/windows/flutter_windows_engine.h"
20 #include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h"
21 #include "flutter/shell/platform/windows/testing/egl/mock_context.h"
22 #include "flutter/shell/platform/windows/testing/egl/mock_manager.h"
23 #include "flutter/shell/platform/windows/testing/egl/mock_window_surface.h"
24 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
25 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
26 #include "flutter/shell/platform/windows/testing/mock_windows_proc_table.h"
27 #include "flutter/shell/platform/windows/testing/test_keyboard.h"
28 #include "flutter/shell/platform/windows/testing/view_modifier.h"
29
30 #include "gmock/gmock.h"
31 #include "gtest/gtest.h"
32
33 namespace flutter {
34 namespace testing {
35
36 using ::testing::_;
37 using ::testing::InSequence;
38 using ::testing::NiceMock;
39 using ::testing::Return;
40
41 constexpr uint64_t kScanCodeKeyA = 0x1e;
42 constexpr uint64_t kVirtualKeyA = 0x41;
43
44 namespace {
45
46 // A struct to use as a FlutterPlatformMessageResponseHandle so it can keep the
47 // callbacks and user data passed to the engine's
48 // PlatformMessageCreateResponseHandle for use in the SendPlatformMessage
49 // overridden function.
50 struct TestResponseHandle {
51 FlutterDesktopBinaryReply callback;
52 void* user_data;
53 };
54
55 static bool test_response = false;
56
57 constexpr uint64_t kKeyEventFromChannel = 0x11;
58 constexpr uint64_t kKeyEventFromEmbedder = 0x22;
59 static std::vector<int> key_event_logs;
60
61 std::unique_ptr<std::vector<uint8_t>> keyHandlingResponse(bool handled) {
62 rapidjson::Document document;
63 auto& allocator = document.GetAllocator();
64 document.SetObject();
65 document.AddMember("handled", test_response, allocator);
66 return flutter::JsonMessageCodec::GetInstance().EncodeMessage(document);
67 }
68
69 // Returns a Flutter project with the required path values to create
70 // a test engine.
71 FlutterProjectBundle GetTestProject() {
72 FlutterDesktopEngineProperties properties = {};
73 properties.assets_path = L"C:\\foo\\flutter_assets";
74 properties.icu_data_path = L"C:\\foo\\icudtl.dat";
75 properties.aot_library_path = L"C:\\foo\\aot.so";
76
77 return FlutterProjectBundle{properties};
78 }
79
80 // Returns an engine instance configured with test project path values, and
81 // overridden methods for sending platform messages, so that the engine can
82 // respond as if the framework were connected.
83 std::unique_ptr<FlutterWindowsEngine> GetTestEngine(
84 std::shared_ptr<WindowsProcTable> windows_proc_table = nullptr) {
85 auto engine = std::make_unique<FlutterWindowsEngine>(
86 GetTestProject(), std::move(windows_proc_table));
87
88 EngineModifier modifier(engine.get());
89
90 auto key_response_controller = std::make_shared<MockKeyResponseController>();
91 key_response_controller->SetChannelResponse(
92 [](MockKeyResponseController::ResponseCallback callback) {
93 key_event_logs.push_back(kKeyEventFromChannel);
94 callback(test_response);
95 });
96 key_response_controller->SetEmbedderResponse(
97 [](const FlutterKeyEvent* event,
98 MockKeyResponseController::ResponseCallback callback) {
99 key_event_logs.push_back(kKeyEventFromEmbedder);
100 callback(test_response);
101 });
102 modifier.embedder_api().NotifyDisplayUpdate =
103 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
104 ([engine_instance = engine.get()](
105 FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
106 const FlutterEngineDisplaysUpdateType update_type,
107 const FlutterEngineDisplay* embedder_displays,
108 size_t display_count) { return kSuccess; }));
109
110 MockEmbedderApiForKeyboard(modifier, key_response_controller);
111
112 engine->Run();
113 return engine;
114 }
115
116 class MockFlutterWindowsEngine : public FlutterWindowsEngine {
117 public:
118 explicit MockFlutterWindowsEngine(
119 std::shared_ptr<WindowsProcTable> windows_proc_table = nullptr)
120 : FlutterWindowsEngine(GetTestProject(), std::move(windows_proc_table)) {}
121
122 MOCK_METHOD(bool, running, (), (const));
123 MOCK_METHOD(bool, Stop, (), ());
124 MOCK_METHOD(bool, PostRasterThreadTask, (fml::closure), (const));
125
126 private:
127 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsEngine);
128 };
129
130 } // namespace
131
132 // Ensure that submenu buttons have their expanded/collapsed status set
133 // apropriately.
134 TEST(FlutterWindowsViewTest, SubMenuExpandedState) {
135 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
136 EngineModifier modifier(engine.get());
137 modifier.embedder_api().UpdateSemanticsEnabled =
138 [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
139 return kSuccess;
140 };
141
142 auto window_binding_handler =
143 std::make_unique<NiceMock<MockWindowBindingHandler>>();
144 std::unique_ptr<FlutterWindowsView> view =
145 engine->CreateView(std::move(window_binding_handler));
146
147 // Enable semantics to instantiate accessibility bridge.
148 view->OnUpdateSemanticsEnabled(true);
149
150 auto bridge = view->accessibility_bridge().lock();
151 ASSERT_TRUE(bridge);
152
153 FlutterSemanticsNode2 root{sizeof(FlutterSemanticsNode2), 0};
154 root.id = 0;
155 root.label = "root";
156 root.hint = "";
157 root.value = "";
158 root.increased_value = "";
159 root.decreased_value = "";
160 root.child_count = 0;
161 root.custom_accessibility_actions_count = 0;
162 root.flags = static_cast<FlutterSemanticsFlag>(
163 FlutterSemanticsFlag::kFlutterSemanticsFlagHasExpandedState |
164 FlutterSemanticsFlag::kFlutterSemanticsFlagIsExpanded);
165 bridge->AddFlutterSemanticsNodeUpdate(root);
166
167 bridge->CommitUpdates();
168
169 {
170 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
171 EXPECT_TRUE(root_node->GetData().HasState(ax::mojom::State::kExpanded));
172
173 // Get the IAccessible for the root node.
174 IAccessible* native_view = root_node->GetNativeViewAccessible();
175 ASSERT_TRUE(native_view != nullptr);
176
177 // Look up against the node itself (not one of its children).
178 VARIANT varchild = {};
179 varchild.vt = VT_I4;
180
181 // Verify the submenu is expanded.
182 varchild.lVal = CHILDID_SELF;
183 VARIANT native_state = {};
184 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
185 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_EXPANDED);
186
187 // Perform similar tests for UIA value;
188 IRawElementProviderSimple* uia_node;
189 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
190 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
191 UIA_ExpandCollapseExpandCollapseStatePropertyId, &native_state)));
192 EXPECT_EQ(native_state.lVal, ExpandCollapseState_Expanded);
193
194 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
195 UIA_AriaPropertiesPropertyId, &native_state)));
196 EXPECT_NE(std::wcsstr(native_state.bstrVal, L"expanded=true"), nullptr);
197 }
198
199 // Test collapsed too.
200 root.flags = static_cast<FlutterSemanticsFlag>(
201 FlutterSemanticsFlag::kFlutterSemanticsFlagHasExpandedState);
202 bridge->AddFlutterSemanticsNodeUpdate(root);
203 bridge->CommitUpdates();
204
205 {
206 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
207 EXPECT_TRUE(root_node->GetData().HasState(ax::mojom::State::kCollapsed));
208
209 // Get the IAccessible for the root node.
210 IAccessible* native_view = root_node->GetNativeViewAccessible();
211 ASSERT_TRUE(native_view != nullptr);
212
213 // Look up against the node itself (not one of its children).
214 VARIANT varchild = {};
215 varchild.vt = VT_I4;
216
217 // Verify the submenu is collapsed.
218 varchild.lVal = CHILDID_SELF;
219 VARIANT native_state = {};
220 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
221 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_COLLAPSED);
222
223 // Perform similar tests for UIA value;
224 IRawElementProviderSimple* uia_node;
225 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
226 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
227 UIA_ExpandCollapseExpandCollapseStatePropertyId, &native_state)));
228 EXPECT_EQ(native_state.lVal, ExpandCollapseState_Collapsed);
229
230 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
231 UIA_AriaPropertiesPropertyId, &native_state)));
232 EXPECT_NE(std::wcsstr(native_state.bstrVal, L"expanded=false"), nullptr);
233 }
234 }
235
236 // The view's surface must be destroyed after the engine is shutdown.
237 // See: https://github.com/flutter/flutter/issues/124463
238 TEST(FlutterWindowsViewTest, Shutdown) {
239 auto engine = std::make_unique<MockFlutterWindowsEngine>();
240 auto window_binding_handler =
241 std::make_unique<NiceMock<MockWindowBindingHandler>>();
242 auto egl_manager = std::make_unique<egl::MockManager>();
243 auto surface = std::make_unique<egl::MockWindowSurface>();
244 auto surface_ptr = surface.get();
245
246 EngineModifier modifier{engine.get()};
247 modifier.SetEGLManager(std::move(egl_manager));
248
249 {
250 std::unique_ptr<FlutterWindowsView> view =
251 engine->CreateView(std::move(window_binding_handler));
252
253 ViewModifier view_modifier{view.get()};
254 view_modifier.SetSurface(std::move(surface));
255
256 // The engine must be stopped before the surface can be destroyed.
257 InSequence s;
258 EXPECT_CALL(*engine.get(), Stop).Times(1);
259 EXPECT_CALL(*surface_ptr, Destroy).Times(1);
260 }
261 }
262
263 TEST(FlutterWindowsViewTest, KeySequence) {
264 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
265
266 test_response = false;
267
268 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
269 std::make_unique<NiceMock<MockWindowBindingHandler>>());
270
271 view->OnKey(kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false, false,
272 [](bool handled) {});
273
274 EXPECT_EQ(key_event_logs.size(), 2);
275 EXPECT_EQ(key_event_logs[0], kKeyEventFromEmbedder);
276 EXPECT_EQ(key_event_logs[1], kKeyEventFromChannel);
277
278 key_event_logs.clear();
279 }
280
281 TEST(FlutterWindowsViewTest, EnableSemantics) {
282 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
283 EngineModifier modifier(engine.get());
284
285 bool semantics_enabled = false;
286 modifier.embedder_api().UpdateSemanticsEnabled = MOCK_ENGINE_PROC(
287 UpdateSemanticsEnabled,
288 [&semantics_enabled](FLUTTER_API_SYMBOL(FlutterEngine) engine,
289 bool enabled) {
290 semantics_enabled = enabled;
291 return kSuccess;
292 });
293
294 auto window_binding_handler =
295 std::make_unique<NiceMock<MockWindowBindingHandler>>();
296 std::unique_ptr<FlutterWindowsView> view =
297 engine->CreateView(std::move(window_binding_handler));
298
299 view->OnUpdateSemanticsEnabled(true);
300 EXPECT_TRUE(semantics_enabled);
301 }
302
303 TEST(FlutterWindowsViewTest, AddSemanticsNodeUpdate) {
304 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
305 EngineModifier modifier(engine.get());
306 modifier.embedder_api().UpdateSemanticsEnabled =
307 [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
308 return kSuccess;
309 };
310
311 auto window_binding_handler =
312 std::make_unique<NiceMock<MockWindowBindingHandler>>();
313 std::unique_ptr<FlutterWindowsView> view =
314 engine->CreateView(std::move(window_binding_handler));
315
316 // Enable semantics to instantiate accessibility bridge.
317 view->OnUpdateSemanticsEnabled(true);
318
319 auto bridge = view->accessibility_bridge().lock();
320 ASSERT_TRUE(bridge);
321
322 // Add root node.
323 FlutterSemanticsNode2 node{sizeof(FlutterSemanticsNode2), 0};
324 node.label = "name";
325 node.value = "value";
326 node.platform_view_id = -1;
327 bridge->AddFlutterSemanticsNodeUpdate(node);
328 bridge->CommitUpdates();
329
330 // Look up the root windows node delegate.
331 auto node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
332 ASSERT_TRUE(node_delegate);
333 EXPECT_EQ(node_delegate->GetChildCount(), 0);
334
335 // Get the native IAccessible object.
336 IAccessible* native_view = node_delegate->GetNativeViewAccessible();
337 ASSERT_TRUE(native_view != nullptr);
338
339 // Property lookups will be made against this node itself.
340 VARIANT varchild{};
341 varchild.vt = VT_I4;
342 varchild.lVal = CHILDID_SELF;
343
344 // Verify node name matches our label.
345 BSTR bname = nullptr;
346 ASSERT_EQ(native_view->get_accName(varchild, &bname), S_OK);
347 std::string name(_com_util::ConvertBSTRToString(bname));
348 EXPECT_EQ(name, "name");
349
350 // Verify node value matches.
351 BSTR bvalue = nullptr;
352 ASSERT_EQ(native_view->get_accValue(varchild, &bvalue), S_OK);
353 std::string value(_com_util::ConvertBSTRToString(bvalue));
354 EXPECT_EQ(value, "value");
355
356 // Verify node type is static text.
357 VARIANT varrole{};
358 varrole.vt = VT_I4;
359 ASSERT_EQ(native_view->get_accRole(varchild, &varrole), S_OK);
360 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT);
361
362 // Get the IRawElementProviderFragment object.
363 IRawElementProviderSimple* uia_view;
364 native_view->QueryInterface(IID_PPV_ARGS(&uia_view));
365 ASSERT_TRUE(uia_view != nullptr);
366
367 // Verify name property matches our label.
368 VARIANT varname{};
369 ASSERT_EQ(uia_view->GetPropertyValue(UIA_NamePropertyId, &varname), S_OK);
370 EXPECT_EQ(varname.vt, VT_BSTR);
371 name = _com_util::ConvertBSTRToString(varname.bstrVal);
372 EXPECT_EQ(name, "name");
373
374 // Verify value property matches our label.
375 VARIANT varvalue{};
376 ASSERT_EQ(uia_view->GetPropertyValue(UIA_ValueValuePropertyId, &varvalue),
377 S_OK);
378 EXPECT_EQ(varvalue.vt, VT_BSTR);
379 value = _com_util::ConvertBSTRToString(varvalue.bstrVal);
380 EXPECT_EQ(value, "value");
381
382 // Verify node control type is text.
383 varrole = {};
384 ASSERT_EQ(uia_view->GetPropertyValue(UIA_ControlTypePropertyId, &varrole),
385 S_OK);
386 EXPECT_EQ(varrole.vt, VT_I4);
387 EXPECT_EQ(varrole.lVal, UIA_TextControlTypeId);
388 }
389
390 // Verify the native IAccessible COM object tree is an accurate reflection of
391 // the platform-agnostic tree. Verify both a root node with children as well as
392 // a non-root node with children, since the AX tree includes special handling
393 // for the root.
394 //
395 // node0
396 // / \
397 // node1 node2
398 // |
399 // node3
400 //
401 // node0 and node2 are grouping nodes. node1 and node2 are static text nodes.
402 TEST(FlutterWindowsViewTest, AddSemanticsNodeUpdateWithChildren) {
403 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
404 EngineModifier modifier(engine.get());
405 modifier.embedder_api().UpdateSemanticsEnabled =
406 [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
407 return kSuccess;
408 };
409
410 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
411 std::make_unique<NiceMock<MockWindowBindingHandler>>());
412
413 // Enable semantics to instantiate accessibility bridge.
414 view->OnUpdateSemanticsEnabled(true);
415
416 auto bridge = view->accessibility_bridge().lock();
417 ASSERT_TRUE(bridge);
418
419 // Add root node.
420 FlutterSemanticsNode2 node0{sizeof(FlutterSemanticsNode2), 0};
421 std::vector<int32_t> node0_children{1, 2};
422 node0.child_count = node0_children.size();
423 node0.children_in_traversal_order = node0_children.data();
424 node0.children_in_hit_test_order = node0_children.data();
425
426 FlutterSemanticsNode2 node1{sizeof(FlutterSemanticsNode2), 1};
427 node1.label = "prefecture";
428 node1.value = "Kyoto";
429 FlutterSemanticsNode2 node2{sizeof(FlutterSemanticsNode2), 2};
430 std::vector<int32_t> node2_children{3};
431 node2.child_count = node2_children.size();
432 node2.children_in_traversal_order = node2_children.data();
433 node2.children_in_hit_test_order = node2_children.data();
434 FlutterSemanticsNode2 node3{sizeof(FlutterSemanticsNode2), 3};
435 node3.label = "city";
436 node3.value = "Uji";
437
438 bridge->AddFlutterSemanticsNodeUpdate(node0);
439 bridge->AddFlutterSemanticsNodeUpdate(node1);
440 bridge->AddFlutterSemanticsNodeUpdate(node2);
441 bridge->AddFlutterSemanticsNodeUpdate(node3);
442 bridge->CommitUpdates();
443
444 // Look up the root windows node delegate.
445 auto node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
446 ASSERT_TRUE(node_delegate);
447 EXPECT_EQ(node_delegate->GetChildCount(), 2);
448
449 // Get the native IAccessible object.
450 IAccessible* node0_accessible = node_delegate->GetNativeViewAccessible();
451 ASSERT_TRUE(node0_accessible != nullptr);
452
453 // Property lookups will be made against this node itself.
454 VARIANT varchild{};
455 varchild.vt = VT_I4;
456 varchild.lVal = CHILDID_SELF;
457
458 // Verify node type is a group.
459 VARIANT varrole{};
460 varrole.vt = VT_I4;
461 ASSERT_EQ(node0_accessible->get_accRole(varchild, &varrole), S_OK);
462 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_GROUPING);
463
464 // Verify child count.
465 long node0_child_count = 0;
466 ASSERT_EQ(node0_accessible->get_accChildCount(&node0_child_count), S_OK);
467 EXPECT_EQ(node0_child_count, 2);
468
469 {
470 // Look up first child of node0 (node1), a static text node.
471 varchild.lVal = 1;
472 IDispatch* node1_dispatch = nullptr;
473 ASSERT_EQ(node0_accessible->get_accChild(varchild, &node1_dispatch), S_OK);
474 ASSERT_TRUE(node1_dispatch != nullptr);
475 IAccessible* node1_accessible = nullptr;
476 ASSERT_EQ(node1_dispatch->QueryInterface(
477 IID_IAccessible, reinterpret_cast<void**>(&node1_accessible)),
478 S_OK);
479 ASSERT_TRUE(node1_accessible != nullptr);
480
481 // Verify node name matches our label.
482 varchild.lVal = CHILDID_SELF;
483 BSTR bname = nullptr;
484 ASSERT_EQ(node1_accessible->get_accName(varchild, &bname), S_OK);
485 std::string name(_com_util::ConvertBSTRToString(bname));
486 EXPECT_EQ(name, "prefecture");
487
488 // Verify node value matches.
489 BSTR bvalue = nullptr;
490 ASSERT_EQ(node1_accessible->get_accValue(varchild, &bvalue), S_OK);
491 std::string value(_com_util::ConvertBSTRToString(bvalue));
492 EXPECT_EQ(value, "Kyoto");
493
494 // Verify node type is static text.
495 VARIANT varrole{};
496 varrole.vt = VT_I4;
497 ASSERT_EQ(node1_accessible->get_accRole(varchild, &varrole), S_OK);
498 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT);
499
500 // Verify the parent node is the root.
501 IDispatch* parent_dispatch;
502 node1_accessible->get_accParent(&parent_dispatch);
503 IAccessible* parent_accessible;
504 ASSERT_EQ(
505 parent_dispatch->QueryInterface(
506 IID_IAccessible, reinterpret_cast<void**>(&parent_accessible)),
507 S_OK);
508 EXPECT_EQ(parent_accessible, node0_accessible);
509 }
510
511 // Look up second child of node0 (node2), a parent group for node3.
512 varchild.lVal = 2;
513 IDispatch* node2_dispatch = nullptr;
514 ASSERT_EQ(node0_accessible->get_accChild(varchild, &node2_dispatch), S_OK);
515 ASSERT_TRUE(node2_dispatch != nullptr);
516 IAccessible* node2_accessible = nullptr;
517 ASSERT_EQ(node2_dispatch->QueryInterface(
518 IID_IAccessible, reinterpret_cast<void**>(&node2_accessible)),
519 S_OK);
520 ASSERT_TRUE(node2_accessible != nullptr);
521
522 {
523 // Verify child count.
524 long node2_child_count = 0;
525 ASSERT_EQ(node2_accessible->get_accChildCount(&node2_child_count), S_OK);
526 EXPECT_EQ(node2_child_count, 1);
527
528 // Verify node type is static text.
529 varchild.lVal = CHILDID_SELF;
530 VARIANT varrole{};
531 varrole.vt = VT_I4;
532 ASSERT_EQ(node2_accessible->get_accRole(varchild, &varrole), S_OK);
533 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_GROUPING);
534
535 // Verify the parent node is the root.
536 IDispatch* parent_dispatch;
537 node2_accessible->get_accParent(&parent_dispatch);
538 IAccessible* parent_accessible;
539 ASSERT_EQ(
540 parent_dispatch->QueryInterface(
541 IID_IAccessible, reinterpret_cast<void**>(&parent_accessible)),
542 S_OK);
543 EXPECT_EQ(parent_accessible, node0_accessible);
544 }
545
546 {
547 // Look up only child of node2 (node3), a static text node.
548 varchild.lVal = 1;
549 IDispatch* node3_dispatch = nullptr;
550 ASSERT_EQ(node2_accessible->get_accChild(varchild, &node3_dispatch), S_OK);
551 ASSERT_TRUE(node3_dispatch != nullptr);
552 IAccessible* node3_accessible = nullptr;
553 ASSERT_EQ(node3_dispatch->QueryInterface(
554 IID_IAccessible, reinterpret_cast<void**>(&node3_accessible)),
555 S_OK);
556 ASSERT_TRUE(node3_accessible != nullptr);
557
558 // Verify node name matches our label.
559 varchild.lVal = CHILDID_SELF;
560 BSTR bname = nullptr;
561 ASSERT_EQ(node3_accessible->get_accName(varchild, &bname), S_OK);
562 std::string name(_com_util::ConvertBSTRToString(bname));
563 EXPECT_EQ(name, "city");
564
565 // Verify node value matches.
566 BSTR bvalue = nullptr;
567 ASSERT_EQ(node3_accessible->get_accValue(varchild, &bvalue), S_OK);
568 std::string value(_com_util::ConvertBSTRToString(bvalue));
569 EXPECT_EQ(value, "Uji");
570
571 // Verify node type is static text.
572 VARIANT varrole{};
573 varrole.vt = VT_I4;
574 ASSERT_EQ(node3_accessible->get_accRole(varchild, &varrole), S_OK);
575 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT);
576
577 // Verify the parent node is node2.
578 IDispatch* parent_dispatch;
579 node3_accessible->get_accParent(&parent_dispatch);
580 IAccessible* parent_accessible;
581 ASSERT_EQ(
582 parent_dispatch->QueryInterface(
583 IID_IAccessible, reinterpret_cast<void**>(&parent_accessible)),
584 S_OK);
585 EXPECT_EQ(parent_accessible, node2_accessible);
586 }
587 }
588
589 // Flutter used to assume that the accessibility root had ID 0.
590 // In a multi-view world, each view has its own accessibility root
591 // with a globally unique node ID.
592 //
593 // node1
594 // |
595 // node2
596 //
597 // node1 is a grouping node, node0 is a static text node.
598 TEST(FlutterWindowsViewTest, NonZeroSemanticsRoot) {
599 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
600 EngineModifier modifier(engine.get());
601 modifier.embedder_api().UpdateSemanticsEnabled =
602 [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
603 return kSuccess;
604 };
605
606 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
607 std::make_unique<NiceMock<MockWindowBindingHandler>>());
608
609 // Enable semantics to instantiate accessibility bridge.
610 view->OnUpdateSemanticsEnabled(true);
611
612 auto bridge = view->accessibility_bridge().lock();
613 ASSERT_TRUE(bridge);
614
615 // Add root node.
616 FlutterSemanticsNode2 node1{sizeof(FlutterSemanticsNode2), 1};
617 std::vector<int32_t> node1_children{2};
618 node1.child_count = node1_children.size();
619 node1.children_in_traversal_order = node1_children.data();
620 node1.children_in_hit_test_order = node1_children.data();
621
622 FlutterSemanticsNode2 node2{sizeof(FlutterSemanticsNode2), 2};
623 node2.label = "prefecture";
624 node2.value = "Kyoto";
625
626 bridge->AddFlutterSemanticsNodeUpdate(node1);
627 bridge->AddFlutterSemanticsNodeUpdate(node2);
628 bridge->CommitUpdates();
629
630 // Look up the root windows node delegate.
631 auto root_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
632 ASSERT_TRUE(root_delegate);
633 EXPECT_EQ(root_delegate->GetChildCount(), 1);
634
635 // Look up the child node delegate
636 auto child_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock();
637 ASSERT_TRUE(child_delegate);
638 EXPECT_EQ(child_delegate->GetChildCount(), 0);
639
640 // Ensure a node with ID 0 does not exist.
641 auto fake_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
642 ASSERT_FALSE(fake_delegate);
643
644 // Get the root's native IAccessible object.
645 IAccessible* node1_accessible = root_delegate->GetNativeViewAccessible();
646 ASSERT_TRUE(node1_accessible != nullptr);
647
648 // Property lookups will be made against this node itself.
649 VARIANT varchild{};
650 varchild.vt = VT_I4;
651 varchild.lVal = CHILDID_SELF;
652
653 // Verify node type is a group.
654 VARIANT varrole{};
655 varrole.vt = VT_I4;
656 ASSERT_EQ(node1_accessible->get_accRole(varchild, &varrole), S_OK);
657 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_GROUPING);
658
659 // Verify child count.
660 long node1_child_count = 0;
661 ASSERT_EQ(node1_accessible->get_accChildCount(&node1_child_count), S_OK);
662 EXPECT_EQ(node1_child_count, 1);
663
664 {
665 // Look up first child of node1 (node0), a static text node.
666 varchild.lVal = 1;
667 IDispatch* node2_dispatch = nullptr;
668 ASSERT_EQ(node1_accessible->get_accChild(varchild, &node2_dispatch), S_OK);
669 ASSERT_TRUE(node2_dispatch != nullptr);
670 IAccessible* node2_accessible = nullptr;
671 ASSERT_EQ(node2_dispatch->QueryInterface(
672 IID_IAccessible, reinterpret_cast<void**>(&node2_accessible)),
673 S_OK);
674 ASSERT_TRUE(node2_accessible != nullptr);
675
676 // Verify node name matches our label.
677 varchild.lVal = CHILDID_SELF;
678 BSTR bname = nullptr;
679 ASSERT_EQ(node2_accessible->get_accName(varchild, &bname), S_OK);
680 std::string name(_com_util::ConvertBSTRToString(bname));
681 EXPECT_EQ(name, "prefecture");
682
683 // Verify node value matches.
684 BSTR bvalue = nullptr;
685 ASSERT_EQ(node2_accessible->get_accValue(varchild, &bvalue), S_OK);
686 std::string value(_com_util::ConvertBSTRToString(bvalue));
687 EXPECT_EQ(value, "Kyoto");
688
689 // Verify node type is static text.
690 VARIANT varrole{};
691 varrole.vt = VT_I4;
692 ASSERT_EQ(node2_accessible->get_accRole(varchild, &varrole), S_OK);
693 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT);
694
695 // Verify the parent node is the root.
696 IDispatch* parent_dispatch;
697 node2_accessible->get_accParent(&parent_dispatch);
698 IAccessible* parent_accessible;
699 ASSERT_EQ(
700 parent_dispatch->QueryInterface(
701 IID_IAccessible, reinterpret_cast<void**>(&parent_accessible)),
702 S_OK);
703 EXPECT_EQ(parent_accessible, node1_accessible);
704 }
705 }
706
707 // Verify the native IAccessible accHitTest method returns the correct
708 // IAccessible COM object for the given coordinates.
709 //
710 // +-----------+
711 // | | |
712 // node0 | | B |
713 // / \ | A |-----|
714 // node1 node2 | | C |
715 // | | | |
716 // node3 +-----------+
717 //
718 // node0 and node2 are grouping nodes. node1 and node2 are static text nodes.
719 //
720 // node0 is located at 0,0 with size 500x500. It spans areas A, B, and C.
721 // node1 is located at 0,0 with size 250x500. It spans area A.
722 // node2 is located at 250,0 with size 250x500. It spans areas B and C.
723 // node3 is located at 250,250 with size 250x250. It spans area C.
724 TEST(FlutterWindowsViewTest, AccessibilityHitTesting) {
725 constexpr FlutterTransformation kIdentityTransform = {1, 0, 0, //
726 0, 1, 0, //
727 0, 0, 1};
728
729 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
730 EngineModifier modifier(engine.get());
731 modifier.embedder_api().UpdateSemanticsEnabled =
732 [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
733 return kSuccess;
734 };
735
736 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
737 std::make_unique<NiceMock<MockWindowBindingHandler>>());
738
739 // Enable semantics to instantiate accessibility bridge.
740 view->OnUpdateSemanticsEnabled(true);
741
742 auto bridge = view->accessibility_bridge().lock();
743 ASSERT_TRUE(bridge);
744
745 // Add root node at origin. Size 500x500.
746 FlutterSemanticsNode2 node0{sizeof(FlutterSemanticsNode2), 0};
747 std::vector<int32_t> node0_children{1, 2};
748 node0.rect = {0, 0, 500, 500};
749 node0.transform = kIdentityTransform;
750 node0.child_count = node0_children.size();
751 node0.children_in_traversal_order = node0_children.data();
752 node0.children_in_hit_test_order = node0_children.data();
753
754 // Add node 1 located at 0,0 relative to node 0. Size 250x500.
755 FlutterSemanticsNode2 node1{sizeof(FlutterSemanticsNode2), 1};
756 node1.rect = {0, 0, 250, 500};
757 node1.transform = kIdentityTransform;
758 node1.label = "prefecture";
759 node1.value = "Kyoto";
760
761 // Add node 2 located at 250,0 relative to node 0. Size 250x500.
762 FlutterSemanticsNode2 node2{sizeof(FlutterSemanticsNode2), 2};
763 std::vector<int32_t> node2_children{3};
764 node2.rect = {0, 0, 250, 500};
765 node2.transform = {1, 0, 250, 0, 1, 0, 0, 0, 1};
766 node2.child_count = node2_children.size();
767 node2.children_in_traversal_order = node2_children.data();
768 node2.children_in_hit_test_order = node2_children.data();
769
770 // Add node 3 located at 0,250 relative to node 2. Size 250, 250.
771 FlutterSemanticsNode2 node3{sizeof(FlutterSemanticsNode2), 3};
772 node3.rect = {0, 0, 250, 250};
773 node3.transform = {1, 0, 0, 0, 1, 250, 0, 0, 1};
774 node3.label = "city";
775 node3.value = "Uji";
776
777 bridge->AddFlutterSemanticsNodeUpdate(node0);
778 bridge->AddFlutterSemanticsNodeUpdate(node1);
779 bridge->AddFlutterSemanticsNodeUpdate(node2);
780 bridge->AddFlutterSemanticsNodeUpdate(node3);
781 bridge->CommitUpdates();
782
783 // Look up the root windows node delegate.
784 auto node0_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
785 ASSERT_TRUE(node0_delegate);
786 auto node1_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
787 ASSERT_TRUE(node1_delegate);
788 auto node2_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock();
789 ASSERT_TRUE(node2_delegate);
790 auto node3_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(3).lock();
791 ASSERT_TRUE(node3_delegate);
792
793 // Get the native IAccessible root object.
794 IAccessible* node0_accessible = node0_delegate->GetNativeViewAccessible();
795 ASSERT_TRUE(node0_accessible != nullptr);
796
797 // Perform a hit test that should hit node 1.
798 VARIANT varchild{};
799 ASSERT_TRUE(SUCCEEDED(node0_accessible->accHitTest(150, 150, &varchild)));
800 EXPECT_EQ(varchild.vt, VT_DISPATCH);
801 EXPECT_EQ(varchild.pdispVal, node1_delegate->GetNativeViewAccessible());
802
803 // Perform a hit test that should hit node 2.
804 varchild = {};
805 ASSERT_TRUE(SUCCEEDED(node0_accessible->accHitTest(450, 150, &varchild)));
806 EXPECT_EQ(varchild.vt, VT_DISPATCH);
807 EXPECT_EQ(varchild.pdispVal, node2_delegate->GetNativeViewAccessible());
808
809 // Perform a hit test that should hit node 3.
810 varchild = {};
811 ASSERT_TRUE(SUCCEEDED(node0_accessible->accHitTest(450, 450, &varchild)));
812 EXPECT_EQ(varchild.vt, VT_DISPATCH);
813 EXPECT_EQ(varchild.pdispVal, node3_delegate->GetNativeViewAccessible());
814 }
815
816 TEST(FlutterWindowsViewTest, WindowResizeTests) {
817 auto windows_proc_table = std::make_shared<NiceMock<MockWindowsProcTable>>();
818 std::unique_ptr<FlutterWindowsEngine> engine =
819 GetTestEngine(windows_proc_table);
820 EngineModifier engine_modifier{engine.get()};
821
822 auto egl_manager = std::make_unique<egl::MockManager>();
823 auto surface = std::make_unique<egl::MockWindowSurface>();
824 auto resized_surface = std::make_unique<egl::MockWindowSurface>();
825 auto resized_surface_ptr = resized_surface.get();
826
827 EXPECT_CALL(*surface.get(), IsValid).WillRepeatedly(Return(true));
828 EXPECT_CALL(*surface.get(), Destroy).WillOnce(Return(true));
829
830 EXPECT_CALL(*egl_manager.get(),
831 CreateWindowSurface(_, /*width=*/500, /*height=*/500))
832 .WillOnce(Return(std::move((resized_surface))));
833 EXPECT_CALL(*resized_surface_ptr, MakeCurrent).WillOnce(Return(true));
834 EXPECT_CALL(*resized_surface_ptr, SetVSyncEnabled).WillOnce(Return(true));
835 EXPECT_CALL(*windows_proc_table.get(), DwmFlush).WillOnce(Return(S_OK));
836
837 EXPECT_CALL(*resized_surface_ptr, Destroy).WillOnce(Return(true));
838
839 engine_modifier.SetEGLManager(std::move(egl_manager));
840
841 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
842 std::make_unique<NiceMock<MockWindowBindingHandler>>());
843
844 ViewModifier view_modifier{view.get()};
845 view_modifier.SetSurface(std::move(surface));
846
847 fml::AutoResetWaitableEvent metrics_sent_latch;
848 engine_modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC(
849 SendWindowMetricsEvent,
850 ([&metrics_sent_latch](auto engine,
851 const FlutterWindowMetricsEvent* event) {
852 metrics_sent_latch.Signal();
853 return kSuccess;
854 }));
855
856 fml::AutoResetWaitableEvent resized_latch;
857 std::thread([&resized_latch, &view]() {
858 // Start the window resize. This sends the new window metrics
859 // and then blocks until another thread completes the window resize.
860 EXPECT_TRUE(view->OnWindowSizeChanged(500, 500));
861 resized_latch.Signal();
862 }).detach();
863
864 // Wait until the platform thread has started the window resize.
865 metrics_sent_latch.Wait();
866
867 // Complete the window resize by reporting a frame with the new window size.
868 ASSERT_TRUE(view->OnFrameGenerated(500, 500));
869 view->OnFramePresented();
870 resized_latch.Wait();
871 }
872
873 // Verify that an empty frame completes a view resize.
874 TEST(FlutterWindowsViewTest, TestEmptyFrameResizes) {
875 auto windows_proc_table = std::make_shared<NiceMock<MockWindowsProcTable>>();
876 std::unique_ptr<FlutterWindowsEngine> engine =
877 GetTestEngine(windows_proc_table);
878 EngineModifier engine_modifier{engine.get()};
879
880 auto egl_manager = std::make_unique<egl::MockManager>();
881 auto surface = std::make_unique<egl::MockWindowSurface>();
882 auto resized_surface = std::make_unique<egl::MockWindowSurface>();
883 auto resized_surface_ptr = resized_surface.get();
884
885 EXPECT_CALL(*surface.get(), IsValid).WillRepeatedly(Return(true));
886 EXPECT_CALL(*surface.get(), Destroy).WillOnce(Return(true));
887
888 EXPECT_CALL(*egl_manager.get(),
889 CreateWindowSurface(_, /*width=*/500, /*height=*/500))
890 .WillOnce(Return(std::move((resized_surface))));
891 EXPECT_CALL(*resized_surface_ptr, MakeCurrent).WillOnce(Return(true));
892 EXPECT_CALL(*resized_surface_ptr, SetVSyncEnabled).WillOnce(Return(true));
893 EXPECT_CALL(*windows_proc_table.get(), DwmFlush).WillOnce(Return(S_OK));
894
895 EXPECT_CALL(*resized_surface_ptr, Destroy).WillOnce(Return(true));
896
897 fml::AutoResetWaitableEvent metrics_sent_latch;
898 engine_modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC(
899 SendWindowMetricsEvent,
900 ([&metrics_sent_latch](auto engine,
901 const FlutterWindowMetricsEvent* event) {
902 metrics_sent_latch.Signal();
903 return kSuccess;
904 }));
905
906 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
907 std::make_unique<NiceMock<MockWindowBindingHandler>>());
908
909 ViewModifier view_modifier{view.get()};
910 engine_modifier.SetEGLManager(std::move(egl_manager));
911 view_modifier.SetSurface(std::move(surface));
912
913 fml::AutoResetWaitableEvent resized_latch;
914 std::thread([&resized_latch, &view]() {
915 // Start the window resize. This sends the new window metrics
916 // and then blocks until another thread completes the window resize.
917 EXPECT_TRUE(view->OnWindowSizeChanged(500, 500));
918 resized_latch.Signal();
919 }).detach();
920
921 // Wait until the platform thread has started the window resize.
922 metrics_sent_latch.Wait();
923
924 // Complete the window resize by reporting an empty frame.
925 view->OnEmptyFrameGenerated();
926 view->OnFramePresented();
927 resized_latch.Wait();
928 }
929
930 // A window resize can be interleaved between a frame generation and
931 // presentation. This should not crash the app. Regression test for:
932 // https://github.com/flutter/flutter/issues/141855
933 TEST(FlutterWindowsViewTest, WindowResizeRace) {
934 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
935 EngineModifier engine_modifier(engine.get());
936
937 auto egl_manager = std::make_unique<egl::MockManager>();
938 auto surface = std::make_unique<egl::MockWindowSurface>();
939
940 EXPECT_CALL(*surface.get(), IsValid).WillRepeatedly(Return(true));
941 EXPECT_CALL(*surface.get(), Destroy).WillOnce(Return(true));
942
943 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
944 std::make_unique<NiceMock<MockWindowBindingHandler>>());
945
946 ViewModifier view_modifier{view.get()};
947 engine_modifier.SetEGLManager(std::move(egl_manager));
948 view_modifier.SetSurface(std::move(surface));
949
950 // Begin a frame.
951 ASSERT_TRUE(view->OnFrameGenerated(100, 100));
952
953 // Inject a window resize between the frame generation and
954 // frame presentation. The new size invalidates the current frame.
955 fml::AutoResetWaitableEvent resized_latch;
956 std::thread([&resized_latch, &view]() {
957 // The resize is never completed. The view times out and returns false.
958 EXPECT_FALSE(view->OnWindowSizeChanged(500, 500));
959 resized_latch.Signal();
960 }).detach();
961
962 // Wait until the platform thread has started the window resize.
963 resized_latch.Wait();
964
965 // Complete the invalidated frame while a resize is pending. Although this
966 // might mean that we presented a frame with the wrong size, this should not
967 // crash the app.
968 view->OnFramePresented();
969 }
970
971 // Window resize should succeed even if the render surface could not be created
972 // even though EGL initialized successfully.
973 TEST(FlutterWindowsViewTest, WindowResizeInvalidSurface) {
974 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
975 EngineModifier engine_modifier(engine.get());
976
977 auto egl_manager = std::make_unique<egl::MockManager>();
978 auto surface = std::make_unique<egl::MockWindowSurface>();
979
980 EXPECT_CALL(*egl_manager.get(), CreateWindowSurface).Times(0);
981 EXPECT_CALL(*surface.get(), IsValid).WillRepeatedly(Return(false));
982 EXPECT_CALL(*surface.get(), Destroy).WillOnce(Return(false));
983
984 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
985 std::make_unique<NiceMock<MockWindowBindingHandler>>());
986
987 ViewModifier view_modifier{view.get()};
988 engine_modifier.SetEGLManager(std::move(egl_manager));
989 view_modifier.SetSurface(std::move(surface));
990
991 auto metrics_sent = false;
992 engine_modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC(
993 SendWindowMetricsEvent,
994 ([&metrics_sent](auto engine, const FlutterWindowMetricsEvent* event) {
995 metrics_sent = true;
996 return kSuccess;
997 }));
998
999 view->OnWindowSizeChanged(500, 500);
1000 }
1001
1002 // Window resize should succeed even if EGL initialized successfully
1003 // but the EGL surface could not be created.
1004 TEST(FlutterWindowsViewTest, WindowResizeWithoutSurface) {
1005 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
1006 EngineModifier modifier(engine.get());
1007
1008 auto egl_manager = std::make_unique<egl::MockManager>();
1009
1010 EXPECT_CALL(*egl_manager.get(), CreateWindowSurface).Times(0);
1011
1012 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1013 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1014
1015 modifier.SetEGLManager(std::move(egl_manager));
1016
1017 auto metrics_sent = false;
1018 modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC(
1019 SendWindowMetricsEvent,
1020 ([&metrics_sent](auto engine, const FlutterWindowMetricsEvent* event) {
1021 metrics_sent = true;
1022 return kSuccess;
1023 }));
1024
1025 view->OnWindowSizeChanged(500, 500);
1026 }
1027
1028 TEST(FlutterWindowsViewTest, WindowRepaintTests) {
1029 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
1030 EngineModifier modifier(engine.get());
1031
1032 FlutterWindowsView view{kImplicitViewId, engine.get(),
1033 std::make_unique<flutter::FlutterWindow>(100, 100)};
1034
1035 bool schedule_frame_called = false;
1036 modifier.embedder_api().ScheduleFrame =
1037 MOCK_ENGINE_PROC(ScheduleFrame, ([&schedule_frame_called](auto engine) {
1038 schedule_frame_called = true;
1039 return kSuccess;
1040 }));
1041
1042 view.OnWindowRepaint();
1043 EXPECT_TRUE(schedule_frame_called);
1044 }
1045
1046 // Ensure that checkboxes have their checked status set apropriately
1047 // Previously, only Radios could have this flag updated
1048 // Resulted in the issue seen at
1049 // https://github.com/flutter/flutter/issues/96218
1050 // This test ensures that the native state of Checkboxes on Windows,
1051 // specifically, is updated as desired.
1052 TEST(FlutterWindowsViewTest, CheckboxNativeState) {
1053 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
1054 EngineModifier modifier(engine.get());
1055 modifier.embedder_api().UpdateSemanticsEnabled =
1056 [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
1057 return kSuccess;
1058 };
1059
1060 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1061 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1062
1063 // Enable semantics to instantiate accessibility bridge.
1064 view->OnUpdateSemanticsEnabled(true);
1065
1066 auto bridge = view->accessibility_bridge().lock();
1067 ASSERT_TRUE(bridge);
1068
1069 FlutterSemanticsNode2 root{sizeof(FlutterSemanticsNode2), 0};
1070 root.id = 0;
1071 root.label = "root";
1072 root.hint = "";
1073 root.value = "";
1074 root.increased_value = "";
1075 root.decreased_value = "";
1076 root.child_count = 0;
1077 root.custom_accessibility_actions_count = 0;
1078 root.flags = static_cast<FlutterSemanticsFlag>(
1079 FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState |
1080 FlutterSemanticsFlag::kFlutterSemanticsFlagIsChecked);
1081 bridge->AddFlutterSemanticsNodeUpdate(root);
1082
1083 bridge->CommitUpdates();
1084
1085 {
1086 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1087 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
1088 EXPECT_EQ(root_node->GetData().GetCheckedState(),
1089 ax::mojom::CheckedState::kTrue);
1090
1091 // Get the IAccessible for the root node.
1092 IAccessible* native_view = root_node->GetNativeViewAccessible();
1093 ASSERT_TRUE(native_view != nullptr);
1094
1095 // Look up against the node itself (not one of its children).
1096 VARIANT varchild = {};
1097 varchild.vt = VT_I4;
1098
1099 // Verify the checkbox is checked.
1100 varchild.lVal = CHILDID_SELF;
1101 VARIANT native_state = {};
1102 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
1103 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_CHECKED);
1104
1105 // Perform similar tests for UIA value;
1106 IRawElementProviderSimple* uia_node;
1107 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1108 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1109 UIA_ToggleToggleStatePropertyId, &native_state)));
1110 EXPECT_EQ(native_state.lVal, ToggleState_On);
1111
1112 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1113 UIA_AriaPropertiesPropertyId, &native_state)));
1114 EXPECT_NE(std::wcsstr(native_state.bstrVal, L"checked=true"), nullptr);
1115 }
1116
1117 // Test unchecked too.
1118 root.flags = static_cast<FlutterSemanticsFlag>(
1119 FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState);
1120 bridge->AddFlutterSemanticsNodeUpdate(root);
1121 bridge->CommitUpdates();
1122
1123 {
1124 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1125 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
1126 EXPECT_EQ(root_node->GetData().GetCheckedState(),
1127 ax::mojom::CheckedState::kFalse);
1128
1129 // Get the IAccessible for the root node.
1130 IAccessible* native_view = root_node->GetNativeViewAccessible();
1131 ASSERT_TRUE(native_view != nullptr);
1132
1133 // Look up against the node itself (not one of its children).
1134 VARIANT varchild = {};
1135 varchild.vt = VT_I4;
1136
1137 // Verify the checkbox is unchecked.
1138 varchild.lVal = CHILDID_SELF;
1139 VARIANT native_state = {};
1140 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
1141 EXPECT_FALSE(native_state.lVal & STATE_SYSTEM_CHECKED);
1142
1143 // Perform similar tests for UIA value;
1144 IRawElementProviderSimple* uia_node;
1145 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1146 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1147 UIA_ToggleToggleStatePropertyId, &native_state)));
1148 EXPECT_EQ(native_state.lVal, ToggleState_Off);
1149
1150 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1151 UIA_AriaPropertiesPropertyId, &native_state)));
1152 EXPECT_NE(std::wcsstr(native_state.bstrVal, L"checked=false"), nullptr);
1153 }
1154
1155 // Now check mixed state.
1156 root.flags = static_cast<FlutterSemanticsFlag>(
1157 FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState |
1158 FlutterSemanticsFlag::kFlutterSemanticsFlagIsCheckStateMixed);
1159 bridge->AddFlutterSemanticsNodeUpdate(root);
1160 bridge->CommitUpdates();
1161
1162 {
1163 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1164 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
1165 EXPECT_EQ(root_node->GetData().GetCheckedState(),
1166 ax::mojom::CheckedState::kMixed);
1167
1168 // Get the IAccessible for the root node.
1169 IAccessible* native_view = root_node->GetNativeViewAccessible();
1170 ASSERT_TRUE(native_view != nullptr);
1171
1172 // Look up against the node itself (not one of its children).
1173 VARIANT varchild = {};
1174 varchild.vt = VT_I4;
1175
1176 // Verify the checkbox is mixed.
1177 varchild.lVal = CHILDID_SELF;
1178 VARIANT native_state = {};
1179 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
1180 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_MIXED);
1181
1182 // Perform similar tests for UIA value;
1183 IRawElementProviderSimple* uia_node;
1184 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1185 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1186 UIA_ToggleToggleStatePropertyId, &native_state)));
1187 EXPECT_EQ(native_state.lVal, ToggleState_Indeterminate);
1188
1189 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1190 UIA_AriaPropertiesPropertyId, &native_state)));
1191 EXPECT_NE(std::wcsstr(native_state.bstrVal, L"checked=mixed"), nullptr);
1192 }
1193 }
1194
1195 // Ensure that switches have their toggle status set apropriately
1196 TEST(FlutterWindowsViewTest, SwitchNativeState) {
1197 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
1198 EngineModifier modifier(engine.get());
1199 modifier.embedder_api().UpdateSemanticsEnabled =
1200 [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
1201 return kSuccess;
1202 };
1203
1204 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1205 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1206
1207 // Enable semantics to instantiate accessibility bridge.
1208 view->OnUpdateSemanticsEnabled(true);
1209
1210 auto bridge = view->accessibility_bridge().lock();
1211 ASSERT_TRUE(bridge);
1212
1213 FlutterSemanticsNode2 root{sizeof(FlutterSemanticsNode2), 0};
1214 root.id = 0;
1215 root.label = "root";
1216 root.hint = "";
1217 root.value = "";
1218 root.increased_value = "";
1219 root.decreased_value = "";
1220 root.child_count = 0;
1221 root.custom_accessibility_actions_count = 0;
1222 root.flags = static_cast<FlutterSemanticsFlag>(
1223 FlutterSemanticsFlag::kFlutterSemanticsFlagHasToggledState |
1224 FlutterSemanticsFlag::kFlutterSemanticsFlagIsToggled);
1225 bridge->AddFlutterSemanticsNodeUpdate(root);
1226
1227 bridge->CommitUpdates();
1228
1229 {
1230 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1231 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kSwitch);
1232 EXPECT_EQ(root_node->GetData().GetCheckedState(),
1233 ax::mojom::CheckedState::kTrue);
1234
1235 // Get the IAccessible for the root node.
1236 IAccessible* native_view = root_node->GetNativeViewAccessible();
1237 ASSERT_TRUE(native_view != nullptr);
1238
1239 // Look up against the node itself (not one of its children).
1240 VARIANT varchild = {};
1241 varchild.vt = VT_I4;
1242
1243 varchild.lVal = CHILDID_SELF;
1244 VARIANT varrole = {};
1245
1246 // Verify the role of the switch is CHECKBUTTON
1247 ASSERT_EQ(native_view->get_accRole(varchild, &varrole), S_OK);
1248 ASSERT_EQ(varrole.lVal, ROLE_SYSTEM_CHECKBUTTON);
1249
1250 // Verify the switch is pressed.
1251 VARIANT native_state = {};
1252 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
1253 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_PRESSED);
1254 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_CHECKED);
1255
1256 // Test similarly on UIA node.
1257 IRawElementProviderSimple* uia_node;
1258 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1259 ASSERT_EQ(uia_node->GetPropertyValue(UIA_ControlTypePropertyId, &varrole),
1260 S_OK);
1261 EXPECT_EQ(varrole.lVal, UIA_ButtonControlTypeId);
1262 ASSERT_EQ(uia_node->GetPropertyValue(UIA_ToggleToggleStatePropertyId,
1263 &native_state),
1264 S_OK);
1265 EXPECT_EQ(native_state.lVal, ToggleState_On);
1266 ASSERT_EQ(
1267 uia_node->GetPropertyValue(UIA_AriaPropertiesPropertyId, &native_state),
1268 S_OK);
1269 EXPECT_NE(std::wcsstr(native_state.bstrVal, L"pressed=true"), nullptr);
1270 }
1271
1272 // Test unpressed too.
1273 root.flags = static_cast<FlutterSemanticsFlag>(
1274 FlutterSemanticsFlag::kFlutterSemanticsFlagHasToggledState);
1275 bridge->AddFlutterSemanticsNodeUpdate(root);
1276 bridge->CommitUpdates();
1277
1278 {
1279 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1280 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kSwitch);
1281 EXPECT_EQ(root_node->GetData().GetCheckedState(),
1282 ax::mojom::CheckedState::kFalse);
1283
1284 // Get the IAccessible for the root node.
1285 IAccessible* native_view = root_node->GetNativeViewAccessible();
1286 ASSERT_TRUE(native_view != nullptr);
1287
1288 // Look up against the node itself (not one of its children).
1289 VARIANT varchild = {};
1290 varchild.vt = VT_I4;
1291
1292 // Verify the switch is not pressed.
1293 varchild.lVal = CHILDID_SELF;
1294 VARIANT native_state = {};
1295 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
1296 EXPECT_FALSE(native_state.lVal & STATE_SYSTEM_PRESSED);
1297 EXPECT_FALSE(native_state.lVal & STATE_SYSTEM_CHECKED);
1298
1299 // Test similarly on UIA node.
1300 IRawElementProviderSimple* uia_node;
1301 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1302 ASSERT_EQ(uia_node->GetPropertyValue(UIA_ToggleToggleStatePropertyId,
1303 &native_state),
1304 S_OK);
1305 EXPECT_EQ(native_state.lVal, ToggleState_Off);
1306 ASSERT_EQ(
1307 uia_node->GetPropertyValue(UIA_AriaPropertiesPropertyId, &native_state),
1308 S_OK);
1309 EXPECT_NE(std::wcsstr(native_state.bstrVal, L"pressed=false"), nullptr);
1310 }
1311 }
1312
1313 TEST(FlutterWindowsViewTest, TooltipNodeData) {
1314 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
1315 EngineModifier modifier(engine.get());
1316 modifier.embedder_api().UpdateSemanticsEnabled =
1317 [](FLUTTER_API_SYMBOL(FlutterEngine) engine, bool enabled) {
1318 return kSuccess;
1319 };
1320
1321 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1322 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1323
1324 // Enable semantics to instantiate accessibility bridge.
1325 view->OnUpdateSemanticsEnabled(true);
1326
1327 auto bridge = view->accessibility_bridge().lock();
1328 ASSERT_TRUE(bridge);
1329
1330 FlutterSemanticsNode2 root{sizeof(FlutterSemanticsNode2), 0};
1331 root.id = 0;
1332 root.label = "root";
1333 root.hint = "";
1334 root.value = "";
1335 root.increased_value = "";
1336 root.decreased_value = "";
1337 root.tooltip = "tooltip";
1338 root.child_count = 0;
1339 root.custom_accessibility_actions_count = 0;
1340 root.flags = static_cast<FlutterSemanticsFlag>(
1341 FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField);
1342 bridge->AddFlutterSemanticsNodeUpdate(root);
1343
1344 bridge->CommitUpdates();
1345 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1346 std::string tooltip = root_node->GetData().GetStringAttribute(
1347 ax::mojom::StringAttribute::kTooltip);
1348 EXPECT_EQ(tooltip, "tooltip");
1349
1350 // Check that MSAA name contains the tooltip.
1351 IAccessible* native_view = bridge->GetFlutterPlatformNodeDelegateFromID(0)
1352 .lock()
1353 ->GetNativeViewAccessible();
1354 VARIANT varchild = {.vt = VT_I4, .lVal = CHILDID_SELF};
1355 BSTR bname;
1356 ASSERT_EQ(native_view->get_accName(varchild, &bname), S_OK);
1357 EXPECT_NE(std::wcsstr(bname, L"tooltip"), nullptr);
1358
1359 // Check that UIA help text is equal to the tooltip.
1360 IRawElementProviderSimple* uia_node;
1361 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1362 VARIANT varname{};
1363 ASSERT_EQ(uia_node->GetPropertyValue(UIA_HelpTextPropertyId, &varname), S_OK);
1364 std::string uia_tooltip = _com_util::ConvertBSTRToString(varname.bstrVal);
1365 EXPECT_EQ(uia_tooltip, "tooltip");
1366 }
1367
1368 // Don't block until the v-blank if it is disabled by the window.
1369 // The surface is updated on the platform thread at startup.
1370 TEST(FlutterWindowsViewTest, DisablesVSyncAtStartup) {
1371 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
1372 auto engine = std::make_unique<MockFlutterWindowsEngine>(windows_proc_table);
1373 auto egl_manager = std::make_unique<egl::MockManager>();
1374 egl::MockContext render_context;
1375 auto surface = std::make_unique<egl::MockWindowSurface>();
1376 auto surface_ptr = surface.get();
1377
1378 EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(false));
1379 EXPECT_CALL(*engine.get(), PostRasterThreadTask).Times(0);
1380
1381 EXPECT_CALL(*windows_proc_table.get(), DwmIsCompositionEnabled)
1382 .WillOnce(Return(true));
1383
1384 EXPECT_CALL(*egl_manager.get(), render_context)
1385 .WillOnce(Return(&render_context));
1386 EXPECT_CALL(*surface_ptr, IsValid).WillOnce(Return(true));
1387
1388 InSequence s;
1389 EXPECT_CALL(*egl_manager.get(), CreateWindowSurface)
1390 .WillOnce(Return(std::move(surface)));
1391 EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true));
1392 EXPECT_CALL(*surface_ptr, SetVSyncEnabled(false)).WillOnce(Return(true));
1393 EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true));
1394
1395 EXPECT_CALL(*engine.get(), Stop).Times(1);
1396 EXPECT_CALL(*surface_ptr, Destroy).Times(1);
1397
1398 EngineModifier modifier{engine.get()};
1399 modifier.SetEGLManager(std::move(egl_manager));
1400
1401 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1402 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1403
1404 view->CreateRenderSurface();
1405 }
1406
1407 // Blocks until the v-blank if it is enabled by the window.
1408 // The surface is updated on the platform thread at startup.
1409 TEST(FlutterWindowsViewTest, EnablesVSyncAtStartup) {
1410 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
1411 auto engine = std::make_unique<MockFlutterWindowsEngine>(windows_proc_table);
1412 auto egl_manager = std::make_unique<egl::MockManager>();
1413 egl::MockContext render_context;
1414 auto surface = std::make_unique<egl::MockWindowSurface>();
1415 auto surface_ptr = surface.get();
1416
1417 EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(false));
1418 EXPECT_CALL(*engine.get(), PostRasterThreadTask).Times(0);
1419 EXPECT_CALL(*windows_proc_table.get(), DwmIsCompositionEnabled)
1420 .WillOnce(Return(false));
1421
1422 EXPECT_CALL(*egl_manager.get(), render_context)
1423 .WillOnce(Return(&render_context));
1424 EXPECT_CALL(*surface_ptr, IsValid).WillOnce(Return(true));
1425
1426 InSequence s;
1427 EXPECT_CALL(*egl_manager.get(), CreateWindowSurface)
1428 .WillOnce(Return(std::move(surface)));
1429 EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true));
1430 EXPECT_CALL(*surface_ptr, SetVSyncEnabled(true)).WillOnce(Return(true));
1431 EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true));
1432
1433 EXPECT_CALL(*engine.get(), Stop).Times(1);
1434 EXPECT_CALL(*surface_ptr, Destroy).Times(1);
1435
1436 EngineModifier modifier{engine.get()};
1437 modifier.SetEGLManager(std::move(egl_manager));
1438
1439 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1440 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1441
1442 view->CreateRenderSurface();
1443 }
1444
1445 // Don't block until the v-blank if it is disabled by the window.
1446 // The surface is updated on the raster thread if the engine is running.
1447 TEST(FlutterWindowsViewTest, DisablesVSyncAfterStartup) {
1448 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
1449 auto engine = std::make_unique<MockFlutterWindowsEngine>(windows_proc_table);
1450 auto egl_manager = std::make_unique<egl::MockManager>();
1451 egl::MockContext render_context;
1452 auto surface = std::make_unique<egl::MockWindowSurface>();
1453 auto surface_ptr = surface.get();
1454
1455 EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(true));
1456 EXPECT_CALL(*windows_proc_table.get(), DwmIsCompositionEnabled)
1457 .WillOnce(Return(true));
1458
1459 EXPECT_CALL(*egl_manager.get(), render_context)
1460 .WillOnce(Return(&render_context));
1461 EXPECT_CALL(*surface_ptr, IsValid).WillOnce(Return(true));
1462
1463 InSequence s;
1464 EXPECT_CALL(*egl_manager.get(), CreateWindowSurface)
1465 .WillOnce(Return(std::move(surface)));
1466 EXPECT_CALL(*engine.get(), PostRasterThreadTask)
1467 .WillOnce([](fml::closure callback) {
1468 callback();
1469 return true;
1470 });
1471 EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true));
1472 EXPECT_CALL(*surface_ptr, SetVSyncEnabled(false)).WillOnce(Return(true));
1473 EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true));
1474 EXPECT_CALL(*engine.get(), Stop).Times(1);
1475 EXPECT_CALL(*surface_ptr, Destroy).Times(1);
1476
1477 EngineModifier modifier{engine.get()};
1478 modifier.SetEGLManager(std::move(egl_manager));
1479
1480 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1481 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1482
1483 view->CreateRenderSurface();
1484 }
1485
1486 // Blocks until the v-blank if it is enabled by the window.
1487 // The surface is updated on the raster thread if the engine is running.
1488 TEST(FlutterWindowsViewTest, EnablesVSyncAfterStartup) {
1489 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
1490 auto engine = std::make_unique<MockFlutterWindowsEngine>(windows_proc_table);
1491 auto egl_manager = std::make_unique<egl::MockManager>();
1492 egl::MockContext render_context;
1493 auto surface = std::make_unique<egl::MockWindowSurface>();
1494 auto surface_ptr = surface.get();
1495
1496 EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(true));
1497
1498 EXPECT_CALL(*windows_proc_table.get(), DwmIsCompositionEnabled)
1499 .WillOnce(Return(false));
1500
1501 EXPECT_CALL(*egl_manager.get(), render_context)
1502 .WillOnce(Return(&render_context));
1503 EXPECT_CALL(*surface_ptr, IsValid).WillOnce(Return(true));
1504
1505 InSequence s;
1506 EXPECT_CALL(*egl_manager.get(), CreateWindowSurface)
1507 .WillOnce(Return(std::move(surface)));
1508 EXPECT_CALL(*engine.get(), PostRasterThreadTask)
1509 .WillOnce([](fml::closure callback) {
1510 callback();
1511 return true;
1512 });
1513
1514 EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true));
1515 EXPECT_CALL(*surface_ptr, SetVSyncEnabled(true)).WillOnce(Return(true));
1516 EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true));
1517 EXPECT_CALL(*engine.get(), Stop).Times(1);
1518 EXPECT_CALL(*surface_ptr, Destroy).Times(1);
1519
1520 EngineModifier modifier{engine.get()};
1521 modifier.SetEGLManager(std::move(egl_manager));
1522
1523 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1524 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1525
1526 view->CreateRenderSurface();
1527 }
1528
1529 // Desktop Window Manager composition can be disabled on Windows 7.
1530 // If this happens, the app must synchronize with the vsync to prevent
1531 // screen tearing.
1532 TEST(FlutterWindowsViewTest, UpdatesVSyncOnDwmUpdates) {
1533 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
1534 auto engine = std::make_unique<MockFlutterWindowsEngine>(windows_proc_table);
1535 auto egl_manager = std::make_unique<egl::MockManager>();
1536 egl::MockContext render_context;
1537 auto surface = std::make_unique<egl::MockWindowSurface>();
1538 auto surface_ptr = surface.get();
1539
1540 EXPECT_CALL(*engine.get(), running).WillRepeatedly(Return(true));
1541
1542 EXPECT_CALL(*engine.get(), PostRasterThreadTask)
1543 .WillRepeatedly([](fml::closure callback) {
1544 callback();
1545 return true;
1546 });
1547
1548 EXPECT_CALL(*windows_proc_table.get(), DwmIsCompositionEnabled)
1549 .WillOnce(Return(false))
1550 .WillOnce(Return(true));
1551
1552 EXPECT_CALL(*egl_manager.get(), render_context)
1553 .WillRepeatedly(Return(&render_context));
1554
1555 EXPECT_CALL(*surface_ptr, IsValid).WillRepeatedly(Return(true));
1556
1557 InSequence s;
1558 EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true));
1559 EXPECT_CALL(*surface_ptr, SetVSyncEnabled(true)).WillOnce(Return(true));
1560 EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true));
1561
1562 EXPECT_CALL(*surface_ptr, MakeCurrent).WillOnce(Return(true));
1563 EXPECT_CALL(*surface_ptr, SetVSyncEnabled(false)).WillOnce(Return(true));
1564 EXPECT_CALL(render_context, ClearCurrent).WillOnce(Return(true));
1565
1566 EXPECT_CALL(*engine.get(), Stop).Times(1);
1567 EXPECT_CALL(*surface_ptr, Destroy).Times(1);
1568
1569 EngineModifier engine_modifier{engine.get()};
1570 engine_modifier.SetEGLManager(std::move(egl_manager));
1571
1572 std::unique_ptr<FlutterWindowsView> view = engine->CreateView(
1573 std::make_unique<NiceMock<MockWindowBindingHandler>>());
1574 ViewModifier view_modifier{view.get()};
1575 view_modifier.SetSurface(std::move(surface));
1576
1577 engine->OnDwmCompositionChanged();
1578 engine->OnDwmCompositionChanged();
1579 }
1580
1581 } // namespace testing
1582 } // namespace flutter
flutter::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:54
FlutterDesktopEngineProperties::aot_library_path
const wchar_t * aot_library_path
Definition: flutter_windows.h:54
flutter_windows_texture_registrar.h
flutter::FlutterWindowsView
Definition: flutter_windows_view.h:34
flutter::JsonMessageCodec::GetInstance
static const JsonMessageCodec & GetInstance()
Definition: json_message_codec.cc:17
FlutterDesktopEngineProperties
Definition: flutter_windows.h:39
flutter::FlutterEngine
Definition: flutter_engine.h:28
FlutterDesktopBinaryReply
void(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
Definition: flutter_messenger.h:26
user_data
void * user_data
Definition: flutter_windows_view_unittests.cc:52
FlutterDesktopEngineProperties::icu_data_path
const wchar_t * icu_data_path
Definition: flutter_windows.h:48
json_message_codec.h
flutter_windows_view.h
flutter_window.h
flutter::testing::kScanCodeKeyA
constexpr uint64_t kScanCodeKeyA
Definition: flutter_windows_view_unittests.cc:41
flutter
Definition: accessibility_bridge_windows.cc:11
flutter_windows_engine.h
flutter::MessageCodec::EncodeMessage
std::unique_ptr< std::vector< uint8_t > > EncodeMessage(const T &message) const
Definition: message_codec.h:45
flutter::testing::TEST
TEST(AccessibilityBridgeWindows, GetParent)
Definition: accessibility_bridge_windows_unittests.cc:237
FlutterDesktopEngineProperties::assets_path
const wchar_t * assets_path
Definition: flutter_windows.h:43
flutter::testing::kVirtualKeyA
constexpr uint64_t kVirtualKeyA
Definition: flutter_windows_view_unittests.cc:42
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:51
node_delegate
std::shared_ptr< FlutterPlatformNodeDelegateWindows > node_delegate
Definition: accessibility_bridge_windows_unittests.cc:33