Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 347850f

Browse files
committed
[Embedder API] Introduce new update semantics callback
This introduces a new update semantics embedder API that is identical to the previous update semantics embedder API, except that its ABI is stable as: 1. The callback uses array of pointers to structs instead of array of structs. This ensures that array indexing does not break if new members are added to the structs. 2. The types `FlutterSemanticsNode` and `FlutterSemanticsCustomAction` were duplicated as `FlutterSemanticsNode2` and `FlutterSemanticsCustomAction2`. This is necessary as `FlutterSemanticsUpdate` has an array of `FlutterSemanticsNode` and an array of `FlutterSemanticsCustomAction`. Adding new members to these structs would break the ABI compatibility of array indexing. This change does not migrate embedders to this new embedder API, this will be done in subsequent changes: flutter/flutter#121176. Part of: flutter/flutter#121176
1 parent 4f74770 commit 347850f

File tree

8 files changed

+691
-36
lines changed

8 files changed

+691
-36
lines changed

shell/platform/common/accessibility_bridge.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,26 @@ AccessibilityBridge::~AccessibilityBridge() {
3636
tree_->RemoveObserver(static_cast<ui::AXTreeObserver*>(this));
3737
}
3838

39+
void AccessibilityBridge::AddFlutterSemanticsNodeUpdate(
40+
const FlutterSemanticsNode2& node) {
41+
pending_semantics_node_updates_[node.id] = FromFlutterSemanticsNode(node);
42+
}
43+
44+
void AccessibilityBridge::AddFlutterSemanticsCustomActionUpdate(
45+
const FlutterSemanticsCustomAction2& action) {
46+
pending_semantics_custom_action_updates_[action.id] =
47+
FromFlutterSemanticsCustomAction(action);
48+
}
49+
50+
// TODO(loicsharma): Remove this as FlutterSemanticsNode is deprecated.
51+
// See: https://github.com/flutter/flutter/issues/121176
3952
void AccessibilityBridge::AddFlutterSemanticsNodeUpdate(
4053
const FlutterSemanticsNode& node) {
4154
pending_semantics_node_updates_[node.id] = FromFlutterSemanticsNode(node);
4255
}
4356

57+
// TODO(loicsharma): Remove this as FlutterSemanticsNode is deprecated.
58+
// See: https://github.com/flutter/flutter/issues/121176
4459
void AccessibilityBridge::AddFlutterSemanticsCustomActionUpdate(
4560
const FlutterSemanticsCustomAction& action) {
4661
pending_semantics_custom_action_updates_[action.id] =
@@ -576,6 +591,74 @@ void AccessibilityBridge::SetTreeData(const SemanticsNode& node,
576591
}
577592
}
578593

594+
AccessibilityBridge::SemanticsNode
595+
AccessibilityBridge::FromFlutterSemanticsNode(
596+
const FlutterSemanticsNode2& flutter_node) {
597+
SemanticsNode result;
598+
result.id = flutter_node.id;
599+
result.flags = flutter_node.flags;
600+
result.actions = flutter_node.actions;
601+
result.text_selection_base = flutter_node.text_selection_base;
602+
result.text_selection_extent = flutter_node.text_selection_extent;
603+
result.scroll_child_count = flutter_node.scroll_child_count;
604+
result.scroll_index = flutter_node.scroll_index;
605+
result.scroll_position = flutter_node.scroll_position;
606+
result.scroll_extent_max = flutter_node.scroll_extent_max;
607+
result.scroll_extent_min = flutter_node.scroll_extent_min;
608+
result.elevation = flutter_node.elevation;
609+
result.thickness = flutter_node.thickness;
610+
if (flutter_node.label) {
611+
result.label = std::string(flutter_node.label);
612+
}
613+
if (flutter_node.hint) {
614+
result.hint = std::string(flutter_node.hint);
615+
}
616+
if (flutter_node.value) {
617+
result.value = std::string(flutter_node.value);
618+
}
619+
if (flutter_node.increased_value) {
620+
result.increased_value = std::string(flutter_node.increased_value);
621+
}
622+
if (flutter_node.decreased_value) {
623+
result.decreased_value = std::string(flutter_node.decreased_value);
624+
}
625+
if (flutter_node.tooltip) {
626+
result.tooltip = std::string(flutter_node.tooltip);
627+
}
628+
result.text_direction = flutter_node.text_direction;
629+
result.rect = flutter_node.rect;
630+
result.transform = flutter_node.transform;
631+
if (flutter_node.child_count > 0) {
632+
result.children_in_traversal_order = std::vector<int32_t>(
633+
flutter_node.children_in_traversal_order,
634+
flutter_node.children_in_traversal_order + flutter_node.child_count);
635+
}
636+
if (flutter_node.custom_accessibility_actions_count > 0) {
637+
result.custom_accessibility_actions = std::vector<int32_t>(
638+
flutter_node.custom_accessibility_actions,
639+
flutter_node.custom_accessibility_actions +
640+
flutter_node.custom_accessibility_actions_count);
641+
}
642+
return result;
643+
}
644+
645+
AccessibilityBridge::SemanticsCustomAction
646+
AccessibilityBridge::FromFlutterSemanticsCustomAction(
647+
const FlutterSemanticsCustomAction2& flutter_custom_action) {
648+
SemanticsCustomAction result;
649+
result.id = flutter_custom_action.id;
650+
result.override_action = flutter_custom_action.override_action;
651+
if (flutter_custom_action.label) {
652+
result.label = std::string(flutter_custom_action.label);
653+
}
654+
if (flutter_custom_action.hint) {
655+
result.hint = std::string(flutter_custom_action.hint);
656+
}
657+
return result;
658+
}
659+
660+
// TODO(loicsharma): Remove this as FlutterSemanticsNode is deprecated.
661+
// See: https://github.com/flutter/flutter/issues/121176
579662
AccessibilityBridge::SemanticsNode
580663
AccessibilityBridge::FromFlutterSemanticsNode(
581664
const FlutterSemanticsNode& flutter_node) {
@@ -627,6 +710,9 @@ AccessibilityBridge::FromFlutterSemanticsNode(
627710
return result;
628711
}
629712

713+
// TODO(loicsharma): Remove this as FlutterSemanticsCustomAction is
714+
// deprecated.
715+
// See: https://github.com/flutter/flutter/issues/121176
630716
AccessibilityBridge::SemanticsCustomAction
631717
AccessibilityBridge::FromFlutterSemanticsCustomAction(
632718
const FlutterSemanticsCustomAction& flutter_custom_action) {

shell/platform/common/accessibility_bridge.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,27 @@ class AccessibilityBridge
5454
/// To flush the pending updates, call the CommitUpdates().
5555
///
5656
/// @param[in] node A reference to the semantics node update.
57+
void AddFlutterSemanticsNodeUpdate(const FlutterSemanticsNode2& node);
58+
59+
//------------------------------------------------------------------------------
60+
/// @brief Adds a custom semantics action update to the pending semantics
61+
/// update. Calling this method alone will NOT update the
62+
/// semantics tree. To flush the pending updates, call the
63+
/// CommitUpdates().
64+
///
65+
/// @param[in] action A reference to the custom semantics action
66+
/// update.
67+
void AddFlutterSemanticsCustomActionUpdate(
68+
const FlutterSemanticsCustomAction2& action);
69+
70+
//------------------------------------------------------------------------------
71+
/// @brief Adds a semantics node update to the pending semantics update.
72+
/// Calling this method alone will NOT update the semantics tree.
73+
/// To flush the pending updates, call the CommitUpdates().
74+
///
75+
/// @param[in] node A reference to the semantics node update.
76+
// TODO(loicsharma): Remove this as FlutterSemanticsNode is deprecated.
77+
// See: https://github.com/flutter/flutter/issues/121176
5778
void AddFlutterSemanticsNodeUpdate(const FlutterSemanticsNode& node);
5879

5980
//------------------------------------------------------------------------------
@@ -64,6 +85,9 @@ class AccessibilityBridge
6485
///
6586
/// @param[in] action A reference to the custom semantics action
6687
/// update.
88+
// TODO(loicsharma): Remove this as FlutterSemanticsCustomAction is
89+
// deprecated.
90+
// See: https://github.com/flutter/flutter/issues/121176
6791
void AddFlutterSemanticsCustomActionUpdate(
6892
const FlutterSemanticsCustomAction& action);
6993

@@ -244,8 +268,19 @@ class AccessibilityBridge
244268
void SetTooltipFromFlutterUpdate(ui::AXNodeData& node_data,
245269
const SemanticsNode& node);
246270
void SetTreeData(const SemanticsNode& node, ui::AXTreeUpdate& tree_update);
271+
SemanticsNode FromFlutterSemanticsNode(
272+
const FlutterSemanticsNode2& flutter_node);
273+
SemanticsCustomAction FromFlutterSemanticsCustomAction(
274+
const FlutterSemanticsCustomAction2& flutter_custom_action);
275+
276+
// TODO(loicsharma): Remove this as FlutterSemanticsNode is deprecated.
277+
// See: https://github.com/flutter/flutter/issues/121176
247278
SemanticsNode FromFlutterSemanticsNode(
248279
const FlutterSemanticsNode& flutter_node);
280+
281+
// TODO(loicsharma): Remove this as FlutterSemanticsCustomAction is
282+
// deprecated.
283+
// See: https://github.com/flutter/flutter/issues/121176
249284
SemanticsCustomAction FromFlutterSemanticsCustomAction(
250285
const FlutterSemanticsCustomAction& flutter_custom_action);
251286

shell/platform/embedder/embedder.cc

Lines changed: 135 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,10 +1268,10 @@ FlutterSemanticsNode CreateEmbedderSemanticsNode(
12681268
transform.get(SkMatrix::kMScaleY), transform.get(SkMatrix::kMTransY),
12691269
transform.get(SkMatrix::kMPersp0), transform.get(SkMatrix::kMPersp1),
12701270
transform.get(SkMatrix::kMPersp2)};
1271+
12711272
// Do not add new members to FlutterSemanticsNode.
12721273
// This would break the forward compatibility of FlutterSemanticsUpdate.
1273-
// TODO(loicsharma): Introduce FlutterSemanticsNode2.
1274-
// https://github.com/flutter/flutter/issues/121176
1274+
// All new members must be added to FlutterSemanticsNode2 instead.
12751275
return {
12761276
sizeof(FlutterSemanticsNode),
12771277
node.id,
@@ -1305,14 +1305,56 @@ FlutterSemanticsNode CreateEmbedderSemanticsNode(
13051305
};
13061306
}
13071307

1308+
// Translates engine semantic nodes to embedder semantic nodes.
1309+
FlutterSemanticsNode2 CreateEmbedderSemanticsNode2(
1310+
const flutter::SemanticsNode& node) {
1311+
SkMatrix transform = node.transform.asM33();
1312+
FlutterTransformation flutter_transform{
1313+
transform.get(SkMatrix::kMScaleX), transform.get(SkMatrix::kMSkewX),
1314+
transform.get(SkMatrix::kMTransX), transform.get(SkMatrix::kMSkewY),
1315+
transform.get(SkMatrix::kMScaleY), transform.get(SkMatrix::kMTransY),
1316+
transform.get(SkMatrix::kMPersp0), transform.get(SkMatrix::kMPersp1),
1317+
transform.get(SkMatrix::kMPersp2)};
1318+
return {
1319+
sizeof(FlutterSemanticsNode2),
1320+
node.id,
1321+
static_cast<FlutterSemanticsFlag>(node.flags),
1322+
static_cast<FlutterSemanticsAction>(node.actions),
1323+
node.textSelectionBase,
1324+
node.textSelectionExtent,
1325+
node.scrollChildren,
1326+
node.scrollIndex,
1327+
node.scrollPosition,
1328+
node.scrollExtentMax,
1329+
node.scrollExtentMin,
1330+
node.elevation,
1331+
node.thickness,
1332+
node.label.c_str(),
1333+
node.hint.c_str(),
1334+
node.value.c_str(),
1335+
node.increasedValue.c_str(),
1336+
node.decreasedValue.c_str(),
1337+
static_cast<FlutterTextDirection>(node.textDirection),
1338+
FlutterRect{node.rect.fLeft, node.rect.fTop, node.rect.fRight,
1339+
node.rect.fBottom},
1340+
flutter_transform,
1341+
node.childrenInTraversalOrder.size(),
1342+
node.childrenInTraversalOrder.data(),
1343+
node.childrenInHitTestOrder.data(),
1344+
node.customAccessibilityActions.size(),
1345+
node.customAccessibilityActions.data(),
1346+
node.platformViewId,
1347+
node.tooltip.c_str(),
1348+
};
1349+
}
1350+
13081351
// Translates engine semantic custom actions to embedder semantic custom
13091352
// actions.
13101353
FlutterSemanticsCustomAction CreateEmbedderSemanticsCustomAction(
13111354
const flutter::CustomAccessibilityAction& action) {
13121355
// Do not add new members to FlutterSemanticsCustomAction.
13131356
// This would break the forward compatibility of FlutterSemanticsUpdate.
1314-
// TODO(loicsharma): Introduce FlutterSemanticsCustomAction2.
1315-
// https://github.com/flutter/flutter/issues/121176
1357+
// All new members must be added to FlutterSemanticsCustomAction2 instead.
13161358
return {
13171359
sizeof(FlutterSemanticsCustomAction),
13181360
action.id,
@@ -1322,8 +1364,21 @@ FlutterSemanticsCustomAction CreateEmbedderSemanticsCustomAction(
13221364
};
13231365
}
13241366

1367+
// Translates engine semantic custom actions to embedder semantic custom
1368+
// actions.
1369+
FlutterSemanticsCustomAction2 CreateEmbedderSemanticsCustomAction2(
1370+
const flutter::CustomAccessibilityAction& action) {
1371+
return {
1372+
sizeof(FlutterSemanticsCustomAction2),
1373+
action.id,
1374+
static_cast<FlutterSemanticsAction>(action.overrideId),
1375+
action.label.c_str(),
1376+
action.hint.c_str(),
1377+
};
1378+
}
1379+
13251380
// Create a callback to notify the embedder of semantic updates
1326-
// using the new embedder callback 'update_semantics_callback'.
1381+
// using the deprecated embedder callback 'update_semantics_callback'.
13271382
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
13281383
CreateNewEmbedderSemanticsUpdateCallback(
13291384
FlutterUpdateSemanticsCallback update_semantics_callback,
@@ -1354,6 +1409,58 @@ CreateNewEmbedderSemanticsUpdateCallback(
13541409
};
13551410
}
13561411

1412+
// Create a callback to notify the embedder of semantic updates
1413+
// using the new embedder callback 'update_semantics_callback2'.
1414+
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
1415+
CreateNewEmbedderSemanticsUpdateCallback2(
1416+
FlutterUpdateSemanticsCallback2 update_semantics_callback,
1417+
void* user_data) {
1418+
return [update_semantics_callback, user_data](
1419+
const flutter::SemanticsNodeUpdates& nodes,
1420+
const flutter::CustomAccessibilityActionUpdates& actions) {
1421+
std::vector<FlutterSemanticsNode2> embedder_nodes;
1422+
std::vector<FlutterSemanticsCustomAction2> embedder_custom_actions;
1423+
1424+
embedder_nodes.reserve(nodes.size());
1425+
embedder_custom_actions.reserve(actions.size());
1426+
1427+
for (const auto& value : nodes) {
1428+
embedder_nodes.push_back(CreateEmbedderSemanticsNode2(value.second));
1429+
}
1430+
1431+
for (const auto& value : actions) {
1432+
embedder_custom_actions.push_back(
1433+
CreateEmbedderSemanticsCustomAction2(value.second));
1434+
}
1435+
1436+
// Provide the embedder an array of pointers to maintain full forward and
1437+
// backward compatibility even if new members are added to semantic structs.
1438+
std::vector<FlutterSemanticsNode2*> embedder_node_pointers;
1439+
std::vector<FlutterSemanticsCustomAction2*> embedder_custom_action_pointers;
1440+
1441+
embedder_node_pointers.reserve(embedder_nodes.size());
1442+
embedder_custom_action_pointers.reserve(embedder_custom_actions.size());
1443+
1444+
for (auto& node : embedder_nodes) {
1445+
embedder_node_pointers.push_back(&node);
1446+
}
1447+
1448+
for (auto& action : embedder_custom_actions) {
1449+
embedder_custom_action_pointers.push_back(&action);
1450+
}
1451+
1452+
FlutterSemanticsUpdate2 update{
1453+
.struct_size = sizeof(FlutterSemanticsUpdate2),
1454+
.node_count = embedder_node_pointers.size(),
1455+
.nodes = embedder_node_pointers.data(),
1456+
.custom_action_count = embedder_custom_action_pointers.size(),
1457+
.custom_actions = embedder_custom_action_pointers.data(),
1458+
};
1459+
1460+
update_semantics_callback(&update, user_data);
1461+
};
1462+
}
1463+
13571464
// Create a callback to notify the embedder of semantic updates
13581465
// using the legacy embedder callbacks 'update_semantics_node_callback' and
13591466
// 'update_semantics_custom_action_callback'.
@@ -1413,6 +1520,11 @@ CreateEmbedderSemanticsUpdateCallback(const FlutterProjectArgs* args,
14131520
// The embedder can register the new callback, or the legacy callbacks, or
14141521
// nothing at all. Handle the case where the embedder registered the 'new'
14151522
// callback.
1523+
if (SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr) {
1524+
return CreateNewEmbedderSemanticsUpdateCallback2(
1525+
args->update_semantics_callback2, user_data);
1526+
}
1527+
14161528
if (SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr) {
14171529
return CreateNewEmbedderSemanticsUpdateCallback(
14181530
args->update_semantics_callback, user_data);
@@ -1590,15 +1702,27 @@ FlutterEngineResult FlutterEngineInitialize(size_t version,
15901702
settings.log_tag = SAFE_ACCESS(args, log_tag, nullptr);
15911703
}
15921704

1593-
if (args->update_semantics_callback != nullptr &&
1594-
(args->update_semantics_node_callback != nullptr ||
1595-
args->update_semantics_custom_action_callback != nullptr)) {
1705+
bool has_update_semantics_2_callback =
1706+
SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr;
1707+
bool has_update_semantics_callback =
1708+
SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr;
1709+
bool has_legacy_update_semantics_callback =
1710+
SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr ||
1711+
SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) !=
1712+
nullptr;
1713+
1714+
int semantic_callback_count = (has_update_semantics_2_callback ? 1 : 0) +
1715+
(has_update_semantics_callback ? 1 : 0) +
1716+
(has_legacy_update_semantics_callback ? 1 : 0);
1717+
1718+
if (semantic_callback_count > 1) {
15961719
return LOG_EMBEDDER_ERROR(
15971720
kInvalidArguments,
15981721
"Multiple semantics update callbacks provided. "
1599-
"Embedders should provide either `update_semantics_callback` "
1600-
"or both `update_semantics_nodes_callback` and "
1601-
"`update_semantics_custom_actions_callback`.");
1722+
"Embedders should provide either `update_semantics_callback2`, "
1723+
"`update_semantics_callback`, or both "
1724+
"`update_semantics_node_callback` and "
1725+
"`update_semantics_custom_action_callback`.");
16021726
}
16031727

16041728
flutter::PlatformViewEmbedder::UpdateSemanticsCallback

0 commit comments

Comments
 (0)