From 5339d083fd372173a51ccda86b2389fb2c601a2d Mon Sep 17 00:00:00 2001 From: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Wed, 26 Mar 2025 13:19:23 -0700 Subject: [PATCH] Fix `-[FlutterView focusItemsInRect:]` crash (#165454) Fixes #163999 The backing semanticsObject of the semantics container (which the container holds a weak ref to) is deallocated after hot restart so `[rootAccessibilityElement accessibilityElementAtIndex:0]` is returning `nil`. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../framework/Source/accessibility_bridge.mm | 2 +- .../Source/accessibility_bridge_test.mm | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index ca7feb93fd..8d35ef97ff 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -67,7 +67,6 @@ AccessibilityBridge::AccessibilityBridge( AccessibilityBridge::~AccessibilityBridge() { [accessibility_channel_ setMessageHandler:nil]; clearState(); - view_controller_.viewIfLoaded.accessibilityElements = nil; } UIView* AccessibilityBridge::textInputView() { @@ -382,6 +381,7 @@ void AccessibilityBridge::clearState() { [objects_ removeAllObjects]; previous_route_id_ = 0; previous_routes_.clear(); + view_controller_.viewIfLoaded.accessibilityElements = nil; } } // namespace flutter diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 8f372f579f..39edc77869 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -2364,4 +2364,41 @@ fml::RefPtr CreateNewThread(const std::string& name) { latch.Wait(); } +- (void)testResetsAccessibilityElementsOnHotRestart { + flutter::MockDelegate mock_delegate; + auto thread = std::make_unique("AccessibilityBridgeTest"); + auto thread_task_runner = thread->GetTaskRunner(); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, + /*platform=*/thread_task_runner, + /*raster=*/thread_task_runner, + /*ui=*/thread_task_runner, + /*io=*/thread_task_runner); + id mockFlutterView = OCMClassMock([FlutterView class]); + id mockFlutterViewController = OCMClassMock([FlutterViewController class]); + OCMStub([mockFlutterViewController viewIfLoaded]).andReturn(mockFlutterView); + + fml::AutoResetWaitableEvent latch; + thread_task_runner->PostTask([&] { + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/nil, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_sync_switch=*/std::make_shared()); + + platform_view->SetOwnerViewController(mockFlutterViewController); + platform_view->SetSemanticsEnabled(true); + + OCMExpect([mockFlutterView setAccessibilityElements:[OCMArg isNil]]); + platform_view->OnPreEngineRestart(); + OCMVerifyAll(mockFlutterView); + + latch.Signal(); + }); + latch.Wait(); +} + @end