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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 26 additions & 34 deletions shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -299,16 +299,11 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
flutterArguments.command_line_argc = static_cast<int>(argv.size());
flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage;
flutterArguments.update_semantics_node_callback = [](const FlutterSemanticsNode* node,
void* user_data) {
flutterArguments.update_semantics_callback = [](const FlutterSemanticsUpdate* update,
void* user_data) {
FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
[engine updateSemanticsNode:node];
[engine updateSemantics:update];
};
flutterArguments.update_semantics_custom_action_callback =
[](const FlutterSemanticsCustomAction* action, void* user_data) {
FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
[engine updateSemanticsCustomActions:action];
};
flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
flutterArguments.shutdown_dart_vm_when_done = true;
flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
Expand Down Expand Up @@ -913,37 +908,34 @@ - (BOOL)unregisterTextureWithID:(int64_t)textureID {
return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
}

- (void)updateSemanticsNode:(const FlutterSemanticsNode*)node {
- (void)updateSemantics:(const FlutterSemanticsUpdate*)update {
NSAssert(_bridge, @"The accessibility bridge must be initialized.");
if (node->id == kFlutterSemanticsNodeIdBatchEnd) {
return;
for (size_t i = 0; i < update->nodes_count; i++) {
const FlutterSemanticsNode* node = &update->nodes[i];
_bridge->AddFlutterSemanticsNodeUpdate(node);
}
_bridge->AddFlutterSemanticsNodeUpdate(node);
}

- (void)updateSemanticsCustomActions:(const FlutterSemanticsCustomAction*)action {
NSAssert(_bridge, @"The accessibility bridge must be initialized.");
if (action->id == kFlutterSemanticsNodeIdBatchEnd) {
// Custom action with id = kFlutterSemanticsNodeIdBatchEnd indicates this is
// the end of the update batch.
_bridge->CommitUpdates();
// Accessibility tree can only be used when the view is loaded.
if (!self.viewController.viewLoaded) {
return;
}
// Attaches the accessibility root to the flutter view.
auto root = _bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
if (root) {
if ([self.viewController.flutterView.accessibilityChildren count] == 0) {
NSAccessibilityElement* native_root = root->GetNativeViewAccessible();
self.viewController.flutterView.accessibilityChildren = @[ native_root ];
}
} else {
self.viewController.flutterView.accessibilityChildren = nil;
}
for (size_t i = 0; i < update->custom_actions_count; i++) {
const FlutterSemanticsCustomAction* action = &update->custom_actions[i];
_bridge->AddFlutterSemanticsCustomActionUpdate(action);
}

_bridge->CommitUpdates();

// Accessibility tree can only be used when the view is loaded.
if (!self.viewController.viewLoaded) {
return;
}
_bridge->AddFlutterSemanticsCustomActionUpdate(action);
// Attaches the accessibility root to the flutter view.
auto root = _bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
if (root) {
if ([self.viewController.flutterView.accessibilityChildren count] == 0) {
NSAccessibilityElement* native_root = root->GetNativeViewAccessible();
self.viewController.flutterView.accessibilityChildren = @[ native_root ];
}
} else {
self.viewController.flutterView.accessibilityChildren = nil;
}
}

#pragma mark - Task runner integration
Expand Down
69 changes: 27 additions & 42 deletions shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,12 @@ @interface FlutterEngine (Test)
FlutterEngine* engine = GetFlutterEngine();
// Capture the update callbacks before the embedder API initializes.
auto original_init = engine.embedderAPI.Initialize;
std::function<void(const FlutterSemanticsNode*, void*)> update_node_callback;
std::function<void(const FlutterSemanticsCustomAction*, void*)> update_action_callback;
std::function<void(const FlutterSemanticsUpdate*, void*)> update_semantics_callback;
engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
Initialize, ([&update_action_callback, &update_node_callback, &original_init](
Initialize, ([&update_semantics_callback, &original_init](
size_t version, const FlutterRendererConfig* config,
const FlutterProjectArgs* args, void* user_data, auto engine_out) {
update_node_callback = args->update_semantics_node_callback;
update_action_callback = args->update_semantics_custom_action_callback;
update_semantics_callback = args->update_semantics_callback;
return original_init(version, config, args, user_data, engine_out);
}));
EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
Expand Down Expand Up @@ -210,7 +208,6 @@ @interface FlutterEngine (Test)
int32_t children[] = {1};
root.children_in_traversal_order = children;
root.custom_accessibility_actions_count = 0;
update_node_callback(&root, (void*)CFBridgingRetain(engine));

FlutterSemanticsNode child1;
child1.id = 1;
Expand All @@ -226,15 +223,13 @@ @interface FlutterEngine (Test)
child1.tooltip = "";
child1.child_count = 0;
child1.custom_accessibility_actions_count = 0;
update_node_callback(&child1, (void*)CFBridgingRetain(engine));

FlutterSemanticsNode node_batch_end;
node_batch_end.id = kFlutterSemanticsNodeIdBatchEnd;
update_node_callback(&node_batch_end, (void*)CFBridgingRetain(engine));

FlutterSemanticsCustomAction action_batch_end;
action_batch_end.id = kFlutterSemanticsNodeIdBatchEnd;
update_action_callback(&action_batch_end, (void*)CFBridgingRetain(engine));
FlutterSemanticsUpdate update;
update.nodes_count = 2;
FlutterSemanticsNode nodes[] = {root, child1};
update.nodes = nodes;
update.custom_actions_count = 0;
update_semantics_callback(&update, (__bridge void*)engine);

// Verify the accessibility tree is attached to the flutter view.
EXPECT_EQ([engine.viewController.flutterView.accessibilityChildren count], 1u);
Expand Down Expand Up @@ -267,14 +262,12 @@ @interface FlutterEngine (Test)
FlutterEngine* engine = GetFlutterEngine();
// Capture the update callbacks before the embedder API initializes.
auto original_init = engine.embedderAPI.Initialize;
std::function<void(const FlutterSemanticsNode*, void*)> update_node_callback;
std::function<void(const FlutterSemanticsCustomAction*, void*)> update_action_callback;
std::function<void(const FlutterSemanticsUpdate*, void*)> update_semantics_callback;
engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
Initialize, ([&update_action_callback, &update_node_callback, &original_init](
Initialize, ([&update_semantics_callback, &original_init](
size_t version, const FlutterRendererConfig* config,
const FlutterProjectArgs* args, void* user_data, auto engine_out) {
update_node_callback = args->update_semantics_node_callback;
update_action_callback = args->update_semantics_custom_action_callback;
update_semantics_callback = args->update_semantics_callback;
return original_init(version, config, args, user_data, engine_out);
}));
EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
Expand Down Expand Up @@ -305,7 +298,6 @@ @interface FlutterEngine (Test)
int32_t children[] = {1};
root.children_in_traversal_order = children;
root.custom_accessibility_actions_count = 0;
update_node_callback(&root, (void*)CFBridgingRetain(engine));

FlutterSemanticsNode child1;
child1.id = 1;
Expand All @@ -321,15 +313,13 @@ @interface FlutterEngine (Test)
child1.tooltip = "";
child1.child_count = 0;
child1.custom_accessibility_actions_count = 0;
update_node_callback(&child1, (void*)CFBridgingRetain(engine));

FlutterSemanticsNode node_batch_end;
node_batch_end.id = kFlutterSemanticsNodeIdBatchEnd;
update_node_callback(&node_batch_end, (void*)CFBridgingRetain(engine));

FlutterSemanticsCustomAction action_batch_end;
action_batch_end.id = kFlutterSemanticsNodeIdBatchEnd;
update_action_callback(&action_batch_end, (void*)CFBridgingRetain(engine));
FlutterSemanticsUpdate update;
update.nodes_count = 2;
FlutterSemanticsNode nodes[] = {root, child1};
update.nodes = nodes;
update.custom_actions_count = 0;
update_semantics_callback(&update, (__bridge void*)engine);

// No crashes.
EXPECT_EQ(engine.viewController, nil);
Expand All @@ -351,14 +341,12 @@ @interface FlutterEngine (Test)
FlutterEngine* engine = GetFlutterEngine();
// Capture the update callbacks before the embedder API initializes.
auto original_init = engine.embedderAPI.Initialize;
std::function<void(const FlutterSemanticsNode*, void*)> update_node_callback;
std::function<void(const FlutterSemanticsCustomAction*, void*)> update_action_callback;
std::function<void(const FlutterSemanticsUpdate*, void*)> update_semantics_callback;
engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
Initialize, ([&update_action_callback, &update_node_callback, &original_init](
Initialize, ([&update_semantics_callback, &original_init](
size_t version, const FlutterRendererConfig* config,
const FlutterProjectArgs* args, void* user_data, auto engine_out) {
update_node_callback = args->update_semantics_node_callback;
update_action_callback = args->update_semantics_custom_action_callback;
update_semantics_callback = args->update_semantics_callback;
return original_init(version, config, args, user_data, engine_out);
}));
EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
Expand Down Expand Up @@ -396,7 +384,6 @@ @interface FlutterEngine (Test)
int32_t children[] = {1};
root.children_in_traversal_order = children;
root.custom_accessibility_actions_count = 0;
update_node_callback(&root, (void*)CFBridgingRetain(engine));

FlutterSemanticsNode child1;
child1.id = 1;
Expand All @@ -412,15 +399,13 @@ @interface FlutterEngine (Test)
child1.tooltip = "";
child1.child_count = 0;
child1.custom_accessibility_actions_count = 0;
update_node_callback(&child1, (void*)CFBridgingRetain(engine));

FlutterSemanticsNode node_batch_end;
node_batch_end.id = kFlutterSemanticsNodeIdBatchEnd;
update_node_callback(&node_batch_end, (void*)CFBridgingRetain(engine));

FlutterSemanticsCustomAction action_batch_end;
action_batch_end.id = kFlutterSemanticsNodeIdBatchEnd;
update_action_callback(&action_batch_end, (void*)CFBridgingRetain(engine));
FlutterSemanticsUpdate update;
update.nodes_count = 2;
FlutterSemanticsNode nodes[] = {root, child1};
update.nodes = nodes;
update.custom_actions_count = 0;
update_semantics_callback(&update, (__bridge void*)engine);

auto native_root = engine.accessibilityBridge.lock()->GetFlutterPlatformNodeDelegateFromID(0);
EXPECT_FALSE(native_root.expired());
Expand Down