Flutter Engine
accessibility_bridge_unittest.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 "accessibility_bridge.h"
6 
7 #include <lib/async-loop/cpp/loop.h>
8 #include <lib/async-loop/default.h>
9 #include <lib/async/cpp/executor.h>
10 #include <lib/fidl/cpp/binding_set.h>
11 #include <lib/fidl/cpp/interface_request.h>
12 #include <lib/inspect/cpp/hierarchy.h>
13 #include <lib/inspect/cpp/inspector.h>
14 #include <lib/inspect/cpp/reader.h>
15 #include <lib/sys/cpp/testing/service_directory_provider.h>
16 #include <lib/ui/scenic/cpp/view_ref_pair.h>
17 #include <zircon/status.h>
18 #include <zircon/types.h>
19 
20 #include <memory>
21 
22 #include "flutter/lib/ui/semantics/semantics_node.h"
23 #include "gtest/gtest.h"
24 
25 #include "flutter_runner_fakes.h"
26 
28 
29 namespace {
30 
31 void ExpectNodeHasRole(
32  const fuchsia::accessibility::semantics::Node& node,
33  const std::unordered_map<uint32_t, fuchsia::accessibility::semantics::Role>
34  roles_by_node_id) {
35  ASSERT_TRUE(node.has_node_id());
36  ASSERT_NE(roles_by_node_id.find(node.node_id()), roles_by_node_id.end());
37  EXPECT_TRUE(node.has_role());
38  EXPECT_EQ(node.role(), roles_by_node_id.at(node.node_id()));
39 }
40 
41 } // namespace
42 
44  public:
45  void SetSemanticsEnabled(bool enabled) { enabled_ = enabled; }
46  void DispatchSemanticsAction(int32_t node_id,
48  actions.push_back(std::make_pair(node_id, action));
49  }
50 
51  bool enabled() { return enabled_; }
52  std::vector<std::pair<int32_t, flutter::SemanticsAction>> actions;
53 
54  private:
55  bool enabled_;
56 };
57 
58 class AccessibilityBridgeTest : public testing::Test {
59  public:
61  : loop_(&kAsyncLoopConfigAttachToCurrentThread),
62  services_provider_(loop_.dispatcher()),
63  executor_(loop_.dispatcher()) {
64  services_provider_.AddService(
65  semantics_manager_.GetHandler(loop_.dispatcher()),
66  SemanticsManager::Name_);
67  }
68 
70  loop_.RunUntilIdle();
71  loop_.ResetQuit();
72  }
73 
74  void RunPromiseToCompletion(fpromise::promise<> promise) {
75  bool done = false;
76  executor_.schedule_task(
77  std::move(promise).and_then([&done]() { done = true; }));
78  while (loop_.GetState() == ASYNC_LOOP_RUNNABLE) {
79  if (done) {
80  loop_.ResetQuit();
81  return;
82  }
83 
84  loop_.Run(zx::deadline_after(zx::duration::infinite()), true);
85  }
86  loop_.ResetQuit();
87  }
88 
89  protected:
90  void SetUp() override {
91  // Connect to SemanticsManager service.
92  fidl::InterfaceHandle<fuchsia::accessibility::semantics::SemanticsManager>
93  semantics_manager;
94  zx_status_t semantics_status =
95  services_provider_.service_directory()
96  ->Connect<fuchsia::accessibility::semantics::SemanticsManager>(
97  semantics_manager.NewRequest());
98  if (semantics_status != ZX_OK) {
99  FML_LOG(WARNING)
100  << "fuchsia::accessibility::semantics::SemanticsManager connection "
101  "failed: "
102  << zx_status_get_string(semantics_status);
103  }
104 
105  accessibility_delegate_.actions.clear();
106  inspector_ = std::make_unique<inspect::Inspector>();
108  set_semantics_enabled_callback = [this](bool enabled) {
109  accessibility_delegate_.SetSemanticsEnabled(enabled);
110  };
112  dispatch_semantics_action_callback =
113  [this](int32_t node_id, flutter::SemanticsAction action) {
114  accessibility_delegate_.DispatchSemanticsAction(node_id, action);
115  };
116  auto [view_ref_control, view_ref] = scenic::ViewRefPair::New();
117  accessibility_bridge_ =
118  std::make_unique<flutter_runner::AccessibilityBridge>(
119  std::move(set_semantics_enabled_callback),
120  std::move(dispatch_semantics_action_callback),
121  std::move(semantics_manager), std::move(view_ref),
122  inspector_->GetRoot().CreateChild("test_node"));
123 
124  RunLoopUntilIdle();
125  }
126 
127  void TearDown() override { semantics_manager_.ResetTree(); }
128 
131  std::unique_ptr<flutter_runner::AccessibilityBridge> accessibility_bridge_;
132  // Required to verify inspect metrics.
133  std::unique_ptr<inspect::Inspector> inspector_;
134 
135  private:
136  async::Loop loop_;
137  sys::testing::ServiceDirectoryProvider services_provider_;
138  // Required to retrieve inspect metrics.
139  async::Executor executor_;
140 };
141 
142 TEST_F(AccessibilityBridgeTest, RegistersViewRef) {
143  EXPECT_TRUE(semantics_manager_.RegisterViewCalled());
144 }
145 
147  EXPECT_FALSE(accessibility_delegate_.enabled());
148  std::unique_ptr<fuchsia::accessibility::semantics::SemanticListener> listener(
149  accessibility_bridge_.release());
150  listener->OnSemanticsModeChanged(true, nullptr);
151  EXPECT_TRUE(accessibility_delegate_.enabled());
152 }
153 
154 TEST_F(AccessibilityBridgeTest, RequestAnnounce) {
155  accessibility_bridge_->RequestAnnounce("message");
156  RunLoopUntilIdle();
157 
158  auto& last_events = semantics_manager_.GetLastEvents();
159  ASSERT_EQ(last_events.size(), 1u);
160  ASSERT_TRUE(last_events[0].is_announce());
161  EXPECT_EQ(last_events[0].announce().message(), "message");
162 }
163 
164 TEST_F(AccessibilityBridgeTest, PopulatesIsKeyboardKeyAttribute) {
166  node0.id = 0;
167  node0.flags = static_cast<int>(flutter::SemanticsFlags::kIsKeyboardKey);
168 
169  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
170  RunLoopUntilIdle();
171 
172  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
173  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
174  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
175  EXPECT_TRUE(fuchsia_node.has_attributes());
176  EXPECT_TRUE(fuchsia_node.attributes().is_keyboard_key());
177 }
178 
179 TEST_F(AccessibilityBridgeTest, UpdatesNodeRoles) {
181 
183  node0.id = 0;
184  node0.flags |= static_cast<int>(flutter::SemanticsFlags::kIsButton);
185  node0.childrenInTraversalOrder = {1, 2, 3, 4, 5, 6, 7, 8};
186  node0.childrenInHitTestOrder = {1, 2, 3, 4, 5, 6, 7, 8};
187  updates.emplace(0, node0);
188 
190  node1.id = 1;
191  node1.flags |= static_cast<int>(flutter::SemanticsFlags::kIsHeader);
192  node1.childrenInTraversalOrder = {};
193  node1.childrenInHitTestOrder = {};
194  updates.emplace(1, node1);
195 
197  node2.id = 2;
198  node2.flags |= static_cast<int>(flutter::SemanticsFlags::kIsImage);
199  node2.childrenInTraversalOrder = {};
200  node2.childrenInHitTestOrder = {};
201  updates.emplace(2, node2);
202 
204  node3.id = 3;
205  node3.flags |= static_cast<int>(flutter::SemanticsFlags::kIsTextField);
206  node3.childrenInTraversalOrder = {};
207  node3.childrenInHitTestOrder = {};
208  updates.emplace(3, node3);
209 
211  node4.childrenInTraversalOrder = {};
212  node4.childrenInHitTestOrder = {};
213  node4.id = 4;
214  node4.flags |= static_cast<int>(flutter::SemanticsFlags::kIsSlider);
215  updates.emplace(4, node4);
216 
218  node5.childrenInTraversalOrder = {};
219  node5.childrenInHitTestOrder = {};
220  node5.id = 5;
221  node5.flags |= static_cast<int>(flutter::SemanticsFlags::kIsLink);
222  updates.emplace(5, node5);
223 
225  node6.childrenInTraversalOrder = {};
226  node6.childrenInHitTestOrder = {};
227  node6.id = 6;
228  node6.flags |= static_cast<int>(flutter::SemanticsFlags::kHasCheckedState);
229  node6.flags |=
231  updates.emplace(6, node6);
232 
234  node7.childrenInTraversalOrder = {};
235  node7.childrenInHitTestOrder = {};
236  node7.id = 7;
237  node7.flags |= static_cast<int>(flutter::SemanticsFlags::kHasCheckedState);
238  updates.emplace(7, node7);
239 
241  node8.childrenInTraversalOrder = {};
242  node8.childrenInHitTestOrder = {};
243  node8.id = 8;
244  node8.flags |= static_cast<int>(flutter::SemanticsFlags::kHasToggledState);
245  updates.emplace(7, node8);
246 
247  accessibility_bridge_->AddSemanticsNodeUpdate(std::move(updates), 1.f);
248  RunLoopUntilIdle();
249 
250  std::unordered_map<uint32_t, fuchsia::accessibility::semantics::Role>
251  roles_by_node_id = {
252  {0u, fuchsia::accessibility::semantics::Role::BUTTON},
253  {1u, fuchsia::accessibility::semantics::Role::HEADER},
254  {2u, fuchsia::accessibility::semantics::Role::IMAGE},
255  {3u, fuchsia::accessibility::semantics::Role::TEXT_FIELD},
256  {4u, fuchsia::accessibility::semantics::Role::SLIDER},
257  {5u, fuchsia::accessibility::semantics::Role::LINK},
258  {6u, fuchsia::accessibility::semantics::Role::RADIO_BUTTON},
259  {7u, fuchsia::accessibility::semantics::Role::CHECK_BOX},
260  {8u, fuchsia::accessibility::semantics::Role::TOGGLE_SWITCH}};
261 
262  EXPECT_EQ(0, semantics_manager_.DeleteCount());
263  EXPECT_EQ(1, semantics_manager_.UpdateCount());
264  EXPECT_EQ(1, semantics_manager_.CommitCount());
265  EXPECT_EQ(8u, semantics_manager_.LastUpdatedNodes().size());
266  for (const auto& node : semantics_manager_.LastUpdatedNodes()) {
267  ExpectNodeHasRole(node, roles_by_node_id);
268  }
269 
270  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
271  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
272 }
273 
274 TEST_F(AccessibilityBridgeTest, DeletesChildrenTransitively) {
275  // Test that when a node is deleted, so are its transitive children.
277  node2.id = 2;
278 
280  node1.id = 1;
281  node1.childrenInTraversalOrder = {2};
282  node1.childrenInHitTestOrder = {2};
283 
285  node0.id = 0;
286  node0.childrenInTraversalOrder = {1};
287  node0.childrenInHitTestOrder = {1};
288 
289  accessibility_bridge_->AddSemanticsNodeUpdate(
290  {
291  {0, node0},
292  {1, node1},
293  {2, node2},
294  },
295  1.f);
296  RunLoopUntilIdle();
297 
298  EXPECT_EQ(0, semantics_manager_.DeleteCount());
299  EXPECT_EQ(1, semantics_manager_.UpdateCount());
300  EXPECT_EQ(1, semantics_manager_.CommitCount());
301  EXPECT_EQ(3U, semantics_manager_.LastUpdatedNodes().size());
302  EXPECT_EQ(0U, semantics_manager_.LastDeletedNodeIds().size());
303  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
304  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
305 
306  // Remove the children
307  node0.childrenInTraversalOrder.clear();
308  node0.childrenInHitTestOrder.clear();
309  accessibility_bridge_->AddSemanticsNodeUpdate(
310  {
311  {0, node0},
312  },
313  1.f);
314  RunLoopUntilIdle();
315 
316  EXPECT_EQ(1, semantics_manager_.DeleteCount());
317  EXPECT_EQ(2, semantics_manager_.UpdateCount());
318  EXPECT_EQ(2, semantics_manager_.CommitCount());
319  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
320  ASSERT_EQ(std::vector<uint32_t>({1, 2}),
321  semantics_manager_.LastDeletedNodeIds());
322  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
323  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
324 }
325 
326 TEST_F(AccessibilityBridgeTest, PopulatesRoleButton) {
328  node0.id = 0;
329  node0.flags = static_cast<int>(flutter::SemanticsFlags::kIsButton);
330 
331  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
332  RunLoopUntilIdle();
333 
334  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
335  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
336  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
337  EXPECT_TRUE(fuchsia_node.has_role());
338  EXPECT_EQ(fuchsia_node.role(),
339  fuchsia::accessibility::semantics::Role::BUTTON);
340 }
341 
342 TEST_F(AccessibilityBridgeTest, PopulatesRoleImage) {
344  node0.id = 0;
345  node0.flags = static_cast<int>(flutter::SemanticsFlags::kIsImage);
346 
347  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
348  RunLoopUntilIdle();
349 
350  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
351  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
352  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
353  EXPECT_TRUE(fuchsia_node.has_role());
354  EXPECT_EQ(fuchsia_node.role(),
355  fuchsia::accessibility::semantics::Role::IMAGE);
356 }
357 
358 TEST_F(AccessibilityBridgeTest, PopulatesRoleSlider) {
360  node0.id = 0;
361  node0.actions |= static_cast<int>(flutter::SemanticsAction::kIncrease);
362 
363  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
364  RunLoopUntilIdle();
365 
366  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
367  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
368  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
369  EXPECT_TRUE(fuchsia_node.has_role());
370  EXPECT_EQ(fuchsia_node.role(),
371  fuchsia::accessibility::semantics::Role::SLIDER);
372 }
373 
374 TEST_F(AccessibilityBridgeTest, PopulatesRoleHeader) {
376  node0.id = 0;
377  node0.flags = static_cast<int>(flutter::SemanticsFlags::kIsHeader);
378 
379  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
380  RunLoopUntilIdle();
381 
382  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
383  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
384  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
385  EXPECT_TRUE(fuchsia_node.has_role());
386  EXPECT_EQ(fuchsia_node.role(),
387  fuchsia::accessibility::semantics::Role::HEADER);
388 }
389 
390 TEST_F(AccessibilityBridgeTest, PopulatesCheckedState) {
392  node0.id = 0;
393  // HasCheckedState = true
394  // IsChecked = true
395  // IsSelected = false
396  node0.flags |= static_cast<int>(flutter::SemanticsFlags::kHasCheckedState);
397  node0.flags |= static_cast<int>(flutter::SemanticsFlags::kIsChecked);
398  node0.value = "value";
399 
400  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
401  RunLoopUntilIdle();
402 
403  EXPECT_EQ(0, semantics_manager_.DeleteCount());
404  EXPECT_EQ(1, semantics_manager_.UpdateCount());
405  EXPECT_EQ(1, semantics_manager_.CommitCount());
406  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
407  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
408  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
409  EXPECT_TRUE(fuchsia_node.has_states());
410  const auto& states = fuchsia_node.states();
411  EXPECT_TRUE(states.has_checked_state());
412  EXPECT_EQ(states.checked_state(),
413  fuchsia::accessibility::semantics::CheckedState::CHECKED);
414  EXPECT_TRUE(states.has_selected());
415  EXPECT_FALSE(states.selected());
416  EXPECT_TRUE(states.has_value());
417  EXPECT_EQ(states.value(), node0.value);
418 
419  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
420  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
421 }
422 
423 TEST_F(AccessibilityBridgeTest, PopulatesSelectedState) {
425  node0.id = 0;
426  // HasCheckedState = false
427  // IsChecked = false
428  // IsSelected = true
429  node0.flags = static_cast<int>(flutter::SemanticsFlags::kIsSelected);
430 
431  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
432  RunLoopUntilIdle();
433 
434  EXPECT_EQ(0, semantics_manager_.DeleteCount());
435  EXPECT_EQ(1, semantics_manager_.UpdateCount());
436  EXPECT_EQ(1, semantics_manager_.CommitCount());
437  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
438  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
439  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
440  EXPECT_TRUE(fuchsia_node.has_states());
441  const auto& states = fuchsia_node.states();
442  EXPECT_TRUE(states.has_checked_state());
443  EXPECT_EQ(states.checked_state(),
444  fuchsia::accessibility::semantics::CheckedState::NONE);
445  EXPECT_TRUE(states.has_selected());
446  EXPECT_TRUE(states.selected());
447 
448  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
449  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
450 }
451 
452 TEST_F(AccessibilityBridgeTest, PopulatesToggledState) {
454  node0.id = 0;
455  node0.flags |= static_cast<int>(flutter::SemanticsFlags::kHasToggledState);
456  node0.flags |= static_cast<int>(flutter::SemanticsFlags::kIsToggled);
457 
458  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
459  RunLoopUntilIdle();
460 
461  EXPECT_EQ(0, semantics_manager_.DeleteCount());
462  EXPECT_EQ(1, semantics_manager_.UpdateCount());
463  EXPECT_EQ(1, semantics_manager_.CommitCount());
464  EXPECT_EQ(1U, semantics_manager_.LastUpdatedNodes().size());
465  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
466  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
467  EXPECT_TRUE(fuchsia_node.has_states());
468  const auto& states = fuchsia_node.states();
469  EXPECT_TRUE(states.has_toggled_state());
470  EXPECT_EQ(states.toggled_state(),
471  fuchsia::accessibility::semantics::ToggledState::ON);
472 
473  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
474  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
475 }
476 
477 TEST_F(AccessibilityBridgeTest, ApplyViewPixelRatioToRoot) {
479  node0.id = 0;
480  node0.flags = static_cast<int>(flutter::SemanticsFlags::kIsSelected);
481 
482  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.25f);
483  RunLoopUntilIdle();
484  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
485  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
486  EXPECT_EQ(fuchsia_node.transform().matrix[0], 0.8f);
487  EXPECT_EQ(fuchsia_node.transform().matrix[5], 0.8f);
488  EXPECT_EQ(fuchsia_node.transform().matrix[10], 1.f);
489 }
490 
491 TEST_F(AccessibilityBridgeTest, DoesNotPopulatesHiddenState) {
492  // Flutter's notion of a hidden node is different from Fuchsia's hidden node.
493  // This test make sures that this state does not get sent.
495  node0.id = 0;
496  // HasCheckedState = false
497  // IsChecked = false
498  // IsSelected = false
499  // IsHidden = true
500  node0.flags = static_cast<int>(flutter::SemanticsFlags::kIsHidden);
501 
502  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
503  RunLoopUntilIdle();
504 
505  EXPECT_EQ(0, semantics_manager_.DeleteCount());
506  EXPECT_EQ(1, semantics_manager_.UpdateCount());
507  EXPECT_EQ(1, semantics_manager_.CommitCount());
508  EXPECT_EQ(1u, semantics_manager_.LastUpdatedNodes().size());
509  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
510  EXPECT_EQ(fuchsia_node.node_id(), static_cast<unsigned int>(node0.id));
511  EXPECT_TRUE(fuchsia_node.has_states());
512  const auto& states = fuchsia_node.states();
513  EXPECT_TRUE(states.has_checked_state());
514  EXPECT_EQ(states.checked_state(),
515  fuchsia::accessibility::semantics::CheckedState::NONE);
516  EXPECT_TRUE(states.has_selected());
517  EXPECT_FALSE(states.selected());
518  EXPECT_FALSE(states.has_hidden());
519 
520  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
521  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
522 }
523 
524 TEST_F(AccessibilityBridgeTest, PopulatesActions) {
526  node0.id = 0;
527  node0.actions |= static_cast<int>(flutter::SemanticsAction::kTap);
528  node0.actions |= static_cast<int>(flutter::SemanticsAction::kLongPress);
529  node0.actions |= static_cast<int>(flutter::SemanticsAction::kShowOnScreen);
530  node0.actions |= static_cast<int>(flutter::SemanticsAction::kIncrease);
531  node0.actions |= static_cast<int>(flutter::SemanticsAction::kDecrease);
532 
533  accessibility_bridge_->AddSemanticsNodeUpdate({{0, node0}}, 1.f);
534  RunLoopUntilIdle();
535 
536  EXPECT_EQ(0, semantics_manager_.DeleteCount());
537  EXPECT_EQ(1, semantics_manager_.UpdateCount());
538  EXPECT_EQ(1, semantics_manager_.CommitCount());
539  EXPECT_EQ(1u, semantics_manager_.LastUpdatedNodes().size());
540  const auto& fuchsia_node = semantics_manager_.LastUpdatedNodes().at(0u);
541  EXPECT_EQ(fuchsia_node.actions().size(), 5u);
542  EXPECT_EQ(fuchsia_node.actions().at(0u),
543  fuchsia::accessibility::semantics::Action::DEFAULT);
544  EXPECT_EQ(fuchsia_node.actions().at(1u),
545  fuchsia::accessibility::semantics::Action::SECONDARY);
546  EXPECT_EQ(fuchsia_node.actions().at(2u),
547  fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN);
548  EXPECT_EQ(fuchsia_node.actions().at(3u),
549  fuchsia::accessibility::semantics::Action::INCREMENT);
550  EXPECT_EQ(fuchsia_node.actions().at(4u),
551  fuchsia::accessibility::semantics::Action::DECREMENT);
552 }
553 
554 TEST_F(AccessibilityBridgeTest, TruncatesLargeLabel) {
555  // Test that labels which are too long are truncated.
557  node0.id = 0;
558 
560  node1.id = 1;
561 
562  flutter::SemanticsNode bad_node;
563  bad_node.id = 2;
564  bad_node.label =
565  std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE + 1, '2');
566 
567  node0.childrenInTraversalOrder = {1, 2};
568  node0.childrenInHitTestOrder = {1, 2};
569 
570  accessibility_bridge_->AddSemanticsNodeUpdate(
571  {
572  {0, node0},
573  {1, node1},
574  {2, bad_node},
575  },
576  1.f);
577  RunLoopUntilIdle();
578 
579  // Nothing to delete, but we should have broken
580  EXPECT_EQ(0, semantics_manager_.DeleteCount());
581  EXPECT_EQ(1, semantics_manager_.UpdateCount());
582  EXPECT_EQ(1, semantics_manager_.CommitCount());
583  EXPECT_EQ(3U, semantics_manager_.LastUpdatedNodes().size());
584  auto trimmed_node =
585  std::find_if(semantics_manager_.LastUpdatedNodes().begin(),
586  semantics_manager_.LastUpdatedNodes().end(),
587  [id = static_cast<uint32_t>(bad_node.id)](
588  fuchsia::accessibility::semantics::Node const& node) {
589  return node.node_id() == id;
590  });
591  ASSERT_NE(trimmed_node, semantics_manager_.LastUpdatedNodes().end());
592  ASSERT_TRUE(trimmed_node->has_attributes());
593  EXPECT_EQ(
594  trimmed_node->attributes().label(),
595  std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE, '2'));
596  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
597  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
598 }
599 
600 TEST_F(AccessibilityBridgeTest, TruncatesLargeToolTip) {
601  // Test that tooltips which are too long are truncated.
603  node0.id = 0;
604 
606  node1.id = 1;
607 
608  flutter::SemanticsNode bad_node;
609  bad_node.id = 2;
610  bad_node.tooltip =
611  std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE + 1, '2');
612 
613  node0.childrenInTraversalOrder = {1, 2};
614  node0.childrenInHitTestOrder = {1, 2};
615 
616  accessibility_bridge_->AddSemanticsNodeUpdate(
617  {
618  {0, node0},
619  {1, node1},
620  {2, bad_node},
621  },
622  1.f);
623  RunLoopUntilIdle();
624 
625  // Nothing to delete, but we should have broken
626  EXPECT_EQ(0, semantics_manager_.DeleteCount());
627  EXPECT_EQ(1, semantics_manager_.UpdateCount());
628  EXPECT_EQ(1, semantics_manager_.CommitCount());
629  EXPECT_EQ(3U, semantics_manager_.LastUpdatedNodes().size());
630  auto trimmed_node =
631  std::find_if(semantics_manager_.LastUpdatedNodes().begin(),
632  semantics_manager_.LastUpdatedNodes().end(),
633  [id = static_cast<uint32_t>(bad_node.id)](
634  fuchsia::accessibility::semantics::Node const& node) {
635  return node.node_id() == id;
636  });
637  ASSERT_NE(trimmed_node, semantics_manager_.LastUpdatedNodes().end());
638  ASSERT_TRUE(trimmed_node->has_attributes());
639  EXPECT_EQ(
640  trimmed_node->attributes().secondary_label(),
641  std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE, '2'));
642  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
643  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
644 }
645 
646 TEST_F(AccessibilityBridgeTest, TruncatesLargeValue) {
647  // Test that values which are too long are truncated.
649  node0.id = 0;
650 
652  node1.id = 1;
653 
654  flutter::SemanticsNode bad_node;
655  bad_node.id = 2;
656  bad_node.value =
657  std::string(fuchsia::accessibility::semantics::MAX_VALUE_SIZE + 1, '2');
658 
659  node0.childrenInTraversalOrder = {1, 2};
660  node0.childrenInHitTestOrder = {1, 2};
661 
662  accessibility_bridge_->AddSemanticsNodeUpdate(
663  {
664  {0, node0},
665  {1, node1},
666  {2, bad_node},
667  },
668  1.f);
669  RunLoopUntilIdle();
670 
671  EXPECT_EQ(0, semantics_manager_.DeleteCount());
672  EXPECT_EQ(1, semantics_manager_.UpdateCount());
673  EXPECT_EQ(1, semantics_manager_.CommitCount());
674  EXPECT_EQ(3U, semantics_manager_.LastUpdatedNodes().size());
675  auto trimmed_node =
676  std::find_if(semantics_manager_.LastUpdatedNodes().begin(),
677  semantics_manager_.LastUpdatedNodes().end(),
678  [id = static_cast<uint32_t>(bad_node.id)](
679  fuchsia::accessibility::semantics::Node const& node) {
680  return node.node_id() == id;
681  });
682  ASSERT_NE(trimmed_node, semantics_manager_.LastUpdatedNodes().end());
683  ASSERT_TRUE(trimmed_node->has_states());
684  EXPECT_EQ(
685  trimmed_node->states().value(),
686  std::string(fuchsia::accessibility::semantics::MAX_VALUE_SIZE, '2'));
687  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
688  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
689 }
690 
691 TEST_F(AccessibilityBridgeTest, SplitsLargeUpdates) {
692  // Test that labels which are too long are truncated.
694  node0.id = 0;
695 
697  node1.id = 1;
698  node1.label =
699  std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE, '1');
700 
702  node2.id = 2;
703  node2.label = "2";
704 
706  node3.id = 3;
707  node3.label = "3";
708 
710  node4.id = 4;
711  node4.value =
712  std::string(fuchsia::accessibility::semantics::MAX_VALUE_SIZE, '4');
713 
714  node0.childrenInTraversalOrder = {1, 2};
715  node0.childrenInHitTestOrder = {1, 2};
716  node1.childrenInTraversalOrder = {3, 4};
717  node1.childrenInHitTestOrder = {3, 4};
718 
719  accessibility_bridge_->AddSemanticsNodeUpdate(
720  {
721  {0, node0},
722  {1, node1},
723  {2, node2},
724  {3, node3},
725  {4, node4},
726  },
727  1.f);
728  RunLoopUntilIdle();
729 
730  // Nothing to delete, but we should have broken into groups (4, 3, 2), (1, 0)
731  EXPECT_EQ(0, semantics_manager_.DeleteCount());
732  EXPECT_EQ(2, semantics_manager_.UpdateCount());
733  EXPECT_EQ(1, semantics_manager_.CommitCount());
734  EXPECT_EQ(2U, semantics_manager_.LastUpdatedNodes().size());
735  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
736  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
737 }
738 
740  // Test that cycles don't cause fatal error.
742  node0.id = 0;
743  node0.childrenInTraversalOrder.push_back(0);
744  node0.childrenInHitTestOrder.push_back(0);
745  accessibility_bridge_->AddSemanticsNodeUpdate(
746  {
747  {0, node0},
748  },
749  1.f);
750  RunLoopUntilIdle();
751 
752  EXPECT_EQ(0, semantics_manager_.DeleteCount());
753  EXPECT_EQ(1, semantics_manager_.UpdateCount());
754  EXPECT_EQ(1, semantics_manager_.CommitCount());
755  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
756  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
757 
758  node0.childrenInTraversalOrder = {0, 1};
759  node0.childrenInHitTestOrder = {0, 1};
761  node1.id = 1;
762  node1.childrenInTraversalOrder = {0};
763  node1.childrenInHitTestOrder = {0};
764  accessibility_bridge_->AddSemanticsNodeUpdate(
765  {
766  {0, node0},
767  {1, node1},
768  },
769  1.f);
770  RunLoopUntilIdle();
771 
772  EXPECT_EQ(0, semantics_manager_.DeleteCount());
773  EXPECT_EQ(2, semantics_manager_.UpdateCount());
774  EXPECT_EQ(2, semantics_manager_.CommitCount());
775  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
776  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
777 }
778 
779 TEST_F(AccessibilityBridgeTest, BatchesLargeMessages) {
780  // Tests that messages get batched appropriately.
782  node0.id = 0;
783 
785 
786  const int32_t child_nodes = 100;
787  const int32_t leaf_nodes = 100;
788  for (int32_t i = 1; i < child_nodes + 1; i++) {
790  node.id = i;
791  node0.childrenInTraversalOrder.push_back(i);
792  node0.childrenInHitTestOrder.push_back(i);
793  for (int32_t j = 0; j < leaf_nodes; j++) {
794  flutter::SemanticsNode leaf_node;
795  int id = (i * child_nodes) + ((j + 1) * leaf_nodes);
796  leaf_node.id = id;
797  leaf_node.label = "A relatively simple label";
798  node.childrenInTraversalOrder.push_back(id);
799  node.childrenInHitTestOrder.push_back(id);
800  update.insert(std::make_pair(id, std::move(leaf_node)));
801  }
802  update.insert(std::make_pair(i, std::move(node)));
803  }
804 
805  update.insert(std::make_pair(0, std::move(node0)));
806  accessibility_bridge_->AddSemanticsNodeUpdate(update, 1.f);
807  RunLoopUntilIdle();
808 
809  EXPECT_EQ(0, semantics_manager_.DeleteCount());
810  EXPECT_TRUE(6 <= semantics_manager_.UpdateCount() &&
811  semantics_manager_.UpdateCount() <= 10);
812  EXPECT_EQ(1, semantics_manager_.CommitCount());
813  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
814  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
815 
816  int next_update_count = semantics_manager_.UpdateCount() + 1;
817  // Remove the children
818  node0.childrenInTraversalOrder.clear();
819  node0.childrenInHitTestOrder.clear();
820  accessibility_bridge_->AddSemanticsNodeUpdate(
821  {
822  {0, node0},
823  },
824  1.f);
825  RunLoopUntilIdle();
826 
827  EXPECT_EQ(1, semantics_manager_.DeleteCount());
828  EXPECT_EQ(next_update_count, semantics_manager_.UpdateCount());
829  EXPECT_EQ(2, semantics_manager_.CommitCount());
830  EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
831  EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
832 }
833 
836  node0.id = 0;
837  node0.rect.setLTRB(0, 0, 100, 100);
838  node0.flags |= static_cast<int32_t>(flutter::SemanticsFlags::kIsFocusable);
839 
841  node1.id = 1;
842  node1.rect.setLTRB(10, 10, 20, 20);
843  // Setting platform view id ensures this node is considered focusable.
844  node1.platformViewId = 1u;
845 
847  node2.id = 2;
848  node2.rect.setLTRB(25, 10, 45, 20);
849  // Setting label ensures this node is considered focusable.
850  node2.label = "label";
851 
853  node3.id = 3;
854  node3.rect.setLTRB(10, 25, 20, 45);
855  // Setting actions to a nonzero value ensures this node is considered
856  // focusable.
857  node3.actions = 1u;
858 
860  node4.id = 4;
861  node4.rect.setLTRB(10, 10, 20, 20);
862  node4.transform.setTranslate(20, 20, 0);
863  node4.flags |= static_cast<int32_t>(flutter::SemanticsFlags::kIsFocusable);
864 
865  node0.childrenInTraversalOrder = {1, 2, 3, 4};
866  node0.childrenInHitTestOrder = {1, 2, 3, 4};
867 
868  accessibility_bridge_->AddSemanticsNodeUpdate(
869  {
870  {0, node0},
871  {1, node1},
872  {2, node2},
873  {3, node3},
874  {4, node4},
875  },
876  1.f);
877  RunLoopUntilIdle();
878 
879  uint32_t hit_node_id;
880  auto callback = [&hit_node_id](fuchsia::accessibility::semantics::Hit hit) {
881  EXPECT_TRUE(hit.has_node_id());
882  hit_node_id = hit.node_id();
883  };
884 
885  // Nodes are:
886  // ----------
887  // | 1 2 |
888  // | 3 4 |
889  // ----------
890 
891  accessibility_bridge_->HitTest({1, 1}, callback);
892  EXPECT_EQ(hit_node_id, 0u);
893  accessibility_bridge_->HitTest({15, 15}, callback);
894  EXPECT_EQ(hit_node_id, 1u);
895  accessibility_bridge_->HitTest({30, 15}, callback);
896  EXPECT_EQ(hit_node_id, 2u);
897  accessibility_bridge_->HitTest({15, 30}, callback);
898  EXPECT_EQ(hit_node_id, 3u);
899  accessibility_bridge_->HitTest({30, 30}, callback);
900  EXPECT_EQ(hit_node_id, 4u);
901 }
902 
903 TEST_F(AccessibilityBridgeTest, HitTestWithPixelRatio) {
905  node0.id = 0;
906  node0.rect.setLTRB(0, 0, 100, 100);
907  node0.flags |= static_cast<int32_t>(flutter::SemanticsFlags::kIsFocusable);
908 
910  node1.id = 1;
911  node1.rect.setLTRB(10, 10, 20, 20);
912  // Setting platform view id ensures this node is considered focusable.
913  node1.platformViewId = 1u;
914 
915  node0.childrenInTraversalOrder = {1};
916  node0.childrenInHitTestOrder = {1};
917 
918  accessibility_bridge_->AddSemanticsNodeUpdate(
919  {
920  {0, node0},
921  {1, node1},
922  },
923  // Pick a very small pixel ratio so that a point within the bounds of
924  // the node's root-space coordinates will be well outside the "screen"
925  // bounds of the node.
926  .1f);
927  RunLoopUntilIdle();
928 
929  uint32_t hit_node_id;
930  auto callback = [&hit_node_id](fuchsia::accessibility::semantics::Hit hit) {
931  EXPECT_TRUE(hit.has_node_id());
932  hit_node_id = hit.node_id();
933  };
934  accessibility_bridge_->HitTest({15, 15}, callback);
935  EXPECT_EQ(hit_node_id, 0u);
936 }
937 
938 TEST_F(AccessibilityBridgeTest, HitTestUnfocusableChild) {
940  node0.id = 0;
941  node0.rect.setLTRB(0, 0, 100, 100);
942 
944  node1.id = 1;
945  node1.rect.setLTRB(10, 10, 60, 60);
946 
948  node2.id = 2;
949  node2.rect.setLTRB(50, 50, 100, 100);
950  node2.flags |= static_cast<int32_t>(flutter::SemanticsFlags::kIsFocusable);
951 
952  node0.childrenInTraversalOrder = {1, 2};
953  node0.childrenInHitTestOrder = {1, 2};
954 
955  accessibility_bridge_->AddSemanticsNodeUpdate(
956  {
957  {0, node0},
958  {1, node1},
959  {2, node2},
960  },
961  1.f);
962  RunLoopUntilIdle();
963 
964  uint32_t hit_node_id;
965  auto callback = [&hit_node_id](fuchsia::accessibility::semantics::Hit hit) {
966  EXPECT_TRUE(hit.has_node_id());
967  hit_node_id = hit.node_id();
968  };
969 
970  accessibility_bridge_->HitTest({55, 55}, callback);
971  EXPECT_EQ(hit_node_id, 2u);
972 }
973 
974 TEST_F(AccessibilityBridgeTest, HitTestOverlapping) {
975  // Tests that the first node in hit test order wins, even if a later node
976  // would be able to receive the hit.
978  node0.id = 0;
979  node0.rect.setLTRB(0, 0, 100, 100);
980  node0.flags |= static_cast<int32_t>(flutter::SemanticsFlags::kIsFocusable);
981 
983  node1.id = 1;
984  node1.rect.setLTRB(0, 0, 100, 100);
985  node1.flags |= static_cast<int32_t>(flutter::SemanticsFlags::kIsFocusable);
986 
988  node2.id = 2;
989  node2.rect.setLTRB(25, 10, 45, 20);
990  node2.flags |= static_cast<int32_t>(flutter::SemanticsFlags::kIsFocusable);
991 
992  node0.childrenInTraversalOrder = {1, 2};
993  node0.childrenInHitTestOrder = {2, 1};
994 
995  accessibility_bridge_->AddSemanticsNodeUpdate(
996  {
997  {0, node0},
998  {1, node1},
999  {2, node2},
1000  },
1001  1.f);
1002  RunLoopUntilIdle();
1003 
1004  uint32_t hit_node_id;
1005  auto callback = [&hit_node_id](fuchsia::accessibility::semantics::Hit hit) {
1006  EXPECT_TRUE(hit.has_node_id());
1007  hit_node_id = hit.node_id();
1008  };
1009 
1010  accessibility_bridge_->HitTest({30, 15}, callback);
1011  EXPECT_EQ(hit_node_id, 2u);
1012 }
1013 
1015  flutter::SemanticsNode node0;
1016  node0.id = 0;
1017 
1018  flutter::SemanticsNode node1;
1019  node1.id = 1;
1020 
1021  node0.childrenInTraversalOrder = {1};
1022  node0.childrenInHitTestOrder = {1};
1023 
1024  accessibility_bridge_->AddSemanticsNodeUpdate(
1025  {
1026  {0, node0},
1027  {1, node1},
1028  },
1029  1.f);
1030  RunLoopUntilIdle();
1031 
1032  auto handled_callback = [](bool handled) { EXPECT_TRUE(handled); };
1033  auto unhandled_callback = [](bool handled) { EXPECT_FALSE(handled); };
1034 
1035  accessibility_bridge_->OnAccessibilityActionRequested(
1036  0u, fuchsia::accessibility::semantics::Action::DEFAULT, handled_callback);
1037  EXPECT_EQ(accessibility_delegate_.actions.size(), 1u);
1038  EXPECT_EQ(accessibility_delegate_.actions.back(),
1039  std::make_pair(0, flutter::SemanticsAction::kTap));
1040 
1041  accessibility_bridge_->OnAccessibilityActionRequested(
1042  0u, fuchsia::accessibility::semantics::Action::SECONDARY,
1043  handled_callback);
1044  EXPECT_EQ(accessibility_delegate_.actions.size(), 2u);
1045  EXPECT_EQ(accessibility_delegate_.actions.back(),
1046  std::make_pair(0, flutter::SemanticsAction::kLongPress));
1047 
1048  accessibility_bridge_->OnAccessibilityActionRequested(
1049  0u, fuchsia::accessibility::semantics::Action::SET_FOCUS,
1050  unhandled_callback);
1051  EXPECT_EQ(accessibility_delegate_.actions.size(), 2u);
1052 
1053  accessibility_bridge_->OnAccessibilityActionRequested(
1054  0u, fuchsia::accessibility::semantics::Action::SET_VALUE,
1055  unhandled_callback);
1056  EXPECT_EQ(accessibility_delegate_.actions.size(), 2u);
1057 
1058  accessibility_bridge_->OnAccessibilityActionRequested(
1059  0u, fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN,
1060  handled_callback);
1061  EXPECT_EQ(accessibility_delegate_.actions.size(), 3u);
1062  EXPECT_EQ(accessibility_delegate_.actions.back(),
1063  std::make_pair(0, flutter::SemanticsAction::kShowOnScreen));
1064 
1065  accessibility_bridge_->OnAccessibilityActionRequested(
1066  2u, fuchsia::accessibility::semantics::Action::DEFAULT,
1067  unhandled_callback);
1068  EXPECT_EQ(accessibility_delegate_.actions.size(), 3u);
1069 
1070  accessibility_bridge_->OnAccessibilityActionRequested(
1071  0u, fuchsia::accessibility::semantics::Action::INCREMENT,
1072  handled_callback);
1073  EXPECT_EQ(accessibility_delegate_.actions.size(), 4u);
1074  EXPECT_EQ(accessibility_delegate_.actions.back(),
1075  std::make_pair(0, flutter::SemanticsAction::kIncrease));
1076 
1077  accessibility_bridge_->OnAccessibilityActionRequested(
1078  0u, fuchsia::accessibility::semantics::Action::DECREMENT,
1079  handled_callback);
1080  EXPECT_EQ(accessibility_delegate_.actions.size(), 5u);
1081  EXPECT_EQ(accessibility_delegate_.actions.back(),
1082  std::make_pair(0, flutter::SemanticsAction::kDecrease));
1083 }
1084 
1085 #if !FLUTTER_RELEASE
1088  flutter::SemanticsNode node0;
1089  node0.id = 0;
1090  node0.label = "node0";
1091  node0.hint = "node0_hint";
1092  node0.value = "value";
1093  node0.flags |= static_cast<int>(flutter::SemanticsFlags::kIsButton);
1094  node0.childrenInTraversalOrder = {1};
1095  node0.childrenInHitTestOrder = {1};
1096  node0.rect.setLTRB(0, 0, 100, 100);
1097  updates.emplace(0, node0);
1098 
1099  flutter::SemanticsNode node1;
1100  node1.id = 1;
1101  node1.flags |= static_cast<int>(flutter::SemanticsFlags::kIsHeader);
1102  node1.childrenInTraversalOrder = {};
1103  node1.childrenInHitTestOrder = {};
1104  updates.emplace(1, node1);
1105 
1106  accessibility_bridge_->AddSemanticsNodeUpdate(std::move(updates), 1.f);
1107  RunLoopUntilIdle();
1108 
1109  fpromise::result<inspect::Hierarchy> hierarchy;
1110  ASSERT_FALSE(hierarchy.is_ok());
1111  RunPromiseToCompletion(
1112  inspect::ReadFromInspector(*inspector_)
1113  .then([&hierarchy](fpromise::result<inspect::Hierarchy>& result) {
1114  hierarchy = std::move(result);
1115  }));
1116  ASSERT_TRUE(hierarchy.is_ok());
1117 
1118  auto tree_inspect_hierarchy = hierarchy.value().GetByPath({"test_node"});
1119  ASSERT_NE(tree_inspect_hierarchy, nullptr);
1120  // TODO(http://fxbug.dev/75841): Rewrite flutter engine accessibility bridge
1121  // tests using inspect matchers. The checks bellow verify that the tree was
1122  // built, and that it matches the format of the input tree. This will be
1123  // updated in the future when test matchers are available to verify individual
1124  // property values.
1125  const auto& root = tree_inspect_hierarchy->children();
1126  ASSERT_EQ(root.size(), 1u);
1127  EXPECT_EQ(root[0].name(), "semantic_tree_root");
1128  const auto& child = root[0].children();
1129  ASSERT_EQ(child.size(), 1u);
1130  EXPECT_EQ(child[0].name(), "node_1");
1131 }
1132 #endif // !FLUTTER_RELEASE
1133 
1134 } // namespace flutter_runner_test
std::vector< int32_t > childrenInHitTestOrder
TEST_F(AccessibilityBridgeTest, RegistersViewRef)
std::unordered_map< int32_t, SemanticsNode > SemanticsNodeUpdates
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:65
std::vector< int32_t > childrenInTraversalOrder
FlKeyEvent FlKeyResponderAsyncCallback callback
std::unique_ptr< flutter_runner::AccessibilityBridge > accessibility_bridge_
void DispatchSemanticsAction(int32_t node_id, flutter::SemanticsAction action)
const FlutterSemanticsNode * node
Definition: fl_view.cc:83
SemanticsAction action
std::function< void(bool)> SetSemanticsEnabledCallback
const char * name
Definition: fuchsia.cc:50
int32_t id
std::function< void(int32_t, flutter::SemanticsAction)> DispatchSemanticsActionCallback
std::vector< std::pair< int32_t, flutter::SemanticsAction > > actions