diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index c9b061d6c93..a7ae7575ca4 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -448,6 +448,11 @@ FlutterEngineResult FlutterEngineRun(size_t version, }; ptr(&embedder_node, user_data); } + const FlutterSemanticsNode batch_end_sentinel = { + sizeof(FlutterSemanticsNode), + kFlutterSemanticsNodeIdBatchEnd, + }; + ptr(&batch_end_sentinel, user_data); }; } @@ -469,6 +474,11 @@ FlutterEngineResult FlutterEngineRun(size_t version, }; ptr(&embedder_action, user_data); } + const FlutterSemanticsCustomAction batch_end_sentinel = { + sizeof(FlutterSemanticsCustomAction), + kFlutterSemanticsCustomActionIdBatchEnd, + }; + ptr(&batch_end_sentinel, user_data); }; } diff --git a/engine/src/flutter/shell/platform/embedder/embedder.h b/engine/src/flutter/shell/platform/embedder/embedder.h index 735d4ff97b9..bff32b3c5d3 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.h +++ b/engine/src/flutter/shell/platform/embedder/embedder.h @@ -319,6 +319,10 @@ typedef struct { double bottom; } FlutterRect; +// |FlutterSemanticsNode| ID used as a sentinel to signal the end of a batch of +// semantics node updates. +const int32_t kFlutterSemanticsNodeIdBatchEnd = -1; + // A node that represents some semantic data. // // The semantics tree is maintained during the semantics phase of the pipeline @@ -386,6 +390,10 @@ typedef struct { const int32_t* custom_accessibility_actions; } FlutterSemanticsNode; +// |FlutterSemanticsCustomAction| ID used as a sentinel to signal the end of a +// batch of semantics custom action updates. +const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1; + // A custom semantics action, or action override. // // Custom actions can be registered by applications in order to provide @@ -496,14 +504,23 @@ typedef struct { // immediately after the root isolate has been created and marked runnable. VoidCallback root_isolate_create_callback; // The callback invoked by the engine in order to give the embedder the - // chance to respond to semantics node updates from the Dart application. The - // callback will be invoked on the thread on which the |FlutterEngineRun| + // chance to respond to semantics node updates from the Dart application. + // Semantics node updates are sent in batches terminated by a 'batch end' + // callback that is passed a sentinel |FlutterSemanticsNode| whose |id| field + // has the value |kFlutterSemanticsNodeIdBatchEnd|. + // + // The callback will be invoked on the thread on which the |FlutterEngineRun| // call is made. FlutterUpdateSemanticsNodeCallback update_semantics_node_callback; // The callback invoked by the engine in order to give the embedder the // chance to respond to updates to semantics custom actions from the Dart - // application. The callback will be invoked on the thread on which the - // |FlutterEngineRun| call is made. + // application. Custom action updates are sent in batches terminated by a + // 'batch end' callback that is passed a sentinel + // |FlutterSemanticsCustomAction| whose |id| field has the value + // |kFlutterSemanticsCustomActionIdBatchEnd|. + // + // The callback will be invoked on the thread on which the |FlutterEngineRun| + // call is made. FlutterUpdateSemanticsCustomActionCallback update_semantics_custom_action_callback; // Path to a directory used to store data that is cached across runs of a diff --git a/engine/src/flutter/shell/platform/embedder/tests/embedder_a11y_unittests.cc b/engine/src/flutter/shell/platform/embedder/tests/embedder_a11y_unittests.cc index 511a1dcf2e2..1959901c197 100644 --- a/engine/src/flutter/shell/platform/embedder/tests/embedder_a11y_unittests.cc +++ b/engine/src/flutter/shell/platform/embedder/tests/embedder_a11y_unittests.cc @@ -151,17 +151,32 @@ TEST(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) { // Wait for UpdateSemantics callback on platform (current) thread. int node_count = 0; + int node_batch_end_count = 0; test_data.on_semantics_update = - [&node_count](const FlutterSemanticsNode* node) { ++node_count; }; + [&node_count, &node_batch_end_count](const FlutterSemanticsNode* node) { + if (node->id == kFlutterSemanticsNodeIdBatchEnd) { + ++node_batch_end_count; + } else { + ++node_count; + } + }; int action_count = 0; + int action_batch_end_count = 0; test_data.on_custom_action_update = - [&action_count](const FlutterSemanticsCustomAction* action) { - ++action_count; + [&action_count, + &action_batch_end_count](const FlutterSemanticsCustomAction* action) { + if (action->id == kFlutterSemanticsCustomActionIdBatchEnd) { + ++action_batch_end_count; + } else { + ++action_count; + } }; g_latch.Wait(); fml::MessageLoop::GetCurrent().RunExpiredTasksNow(); ASSERT_EQ(4, node_count); + ASSERT_EQ(1, node_batch_end_count); ASSERT_EQ(1, action_count); + ASSERT_EQ(1, action_batch_end_count); // Dispatch a tap to semantics node 42. Wait for NotifySemanticsAction. g_test_data_callback = [](Dart_NativeArguments args) {