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 
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"
17 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.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 {
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);
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