Fix crash in iOS accessibility (flutter/engine#3706)

* Fix crash in iOS accessbility

Previously, whenever an action would remove the focused element from the screen the app would crash. This change tells iOS to focus the first on-screen element after every change to the semantics tree. This avoids the crash.

In a future iteration, we should tell iOS which element it needs to focus by looking at the sematnics tree (instead of leaving it up to the iOS).

* Better handle the case were the focused element stays on the screen

* review comments
This commit is contained in:
Michael Goderbauer
2017-05-23 13:14:56 -07:00
committed by GitHub
parent 24d5ede97c
commit e1cadccf79
2 changed files with 30 additions and 11 deletions

View File

@@ -55,7 +55,7 @@ class AccessibilityBridge final {
private:
SemanticsObject* GetOrCreateObject(int32_t id);
void VisitObjectsRecursively(SemanticsObject* object, std::unordered_set<int>* visited_objects);
void ReleaseObjects(const std::unordered_map<int, SemanticsObject*>& objects);
void ReleaseObjects(std::unordered_map<int, SemanticsObject*>& objects);
UIView* view_;
PlatformViewIOS* platform_view_;

View File

@@ -234,6 +234,14 @@ void AccessibilityBridge::UpdateSemantics(std::vector<blink::SemanticsNode> node
SemanticsObject* root = objects_[kRootNodeId];
if (root) {
if (!view_.accessibilityElements) {
view_.accessibilityElements = @[ root ];
}
} else {
view_.accessibilityElements = nil;
}
std::unordered_set<int> visited_objects;
if (root)
VisitObjectsRecursively(root, &visited_objects);
@@ -247,17 +255,27 @@ void AccessibilityBridge::UpdateSemantics(std::vector<blink::SemanticsNode> node
// TODO(abarth): Use extract once we're at C++17.
}
ReleaseObjects(doomed_objects);
doomed_objects.clear();
if (root) {
if (!view_.accessibilityElements) {
view_.accessibilityElements = @[ root ];
SemanticsObject* doomed_focused_object = nil;
for (const auto& entry : doomed_objects) {
SemanticsObject* object = entry.second;
if ([object accessibilityElementIsFocused]) {
doomed_focused_object = object;
break;
}
} else {
view_.accessibilityElements = nil;
}
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
if (doomed_focused_object != nil) {
// Previously focused element is no longer in the tree.
// Passing `nil` as argument to let iOS figure out what to focus next.
// TODO(goderbauer): Figure out which element should be focused next and post
// UIAccessibilityLayoutChangedNotification with that element instead.
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
} else {
// Passing `nil` as argument to keep focus where it is.
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
ReleaseObjects(doomed_objects);
}
void AccessibilityBridge::DispatchSemanticsAction(int32_t uid, blink::SemanticsAction action) {
@@ -280,12 +298,13 @@ void AccessibilityBridge::VisitObjectsRecursively(SemanticsObject* object,
VisitObjectsRecursively(child, visited_objects);
}
void AccessibilityBridge::ReleaseObjects(const std::unordered_map<int, SemanticsObject*>& objects) {
void AccessibilityBridge::ReleaseObjects(std::unordered_map<int, SemanticsObject*>& objects) {
for (const auto& entry : objects) {
SemanticsObject* object = entry.second;
[object neuter];
[object release];
}
objects.clear();
}
} // namespace shell