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

Commit f8ecbc0

Browse files
authored
[Windows] Add/remove view failures should not hang (#52164)
If the embedder API's `FlutterEngineAddView` and `FlutterEngineRemoveView` don't return `kSuccess`, their callbacks won't be invoked. See: [flutter.dev/go/multi-view-embedder-apis](https://flutter.dev/go/multi-view-embedder-apis). Previously, the Windows embedder would hang in this scenario as it blocks until the callbacks are invoked. Now, the Windows embedder only blocks if these embedder APIs return `kSuccess`. Kudos to @dkwingsmt for catching this! Part of flutter/flutter#144810 Part of flutter/flutter#142845 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 818191d commit f8ecbc0

File tree

2 files changed

+70
-5
lines changed

2 files changed

+70
-5
lines changed

shell/platform/windows/flutter_windows_engine.cc

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,15 @@ std::unique_ptr<FlutterWindowsView> FlutterWindowsEngine::CreateView(
532532
captures->latch.Signal();
533533
};
534534

535-
embedder_api_.AddView(engine_, &info);
535+
FlutterEngineResult result = embedder_api_.AddView(engine_, &info);
536+
if (result != kSuccess) {
537+
FML_LOG(ERROR)
538+
<< "Starting the add view operation failed. FlutterEngineAddView "
539+
"returned an unexpected result: "
540+
<< result << ". This indicates a bug in the Windows embedder.";
541+
FML_DCHECK(false);
542+
return nullptr;
543+
}
536544

537545
// Block the platform thread until the engine has added the view.
538546
// TODO(loicsharma): This blocks the platform thread eagerly and can
@@ -573,15 +581,24 @@ void FlutterWindowsEngine::RemoveView(FlutterViewId view_id) {
573581
info.view_id = view_id;
574582
info.user_data = &captures;
575583
info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
576-
// This is invoked on the raster thread, the same thread that the present
577-
// callback is invoked. If |FlutterRemoveViewResult.removed| is `true`,
578-
// the engine guarantees the view won't be presented.
584+
// This is invoked on an engine thread. If
585+
// |FlutterRemoveViewResult.removed| is `true`, the engine guarantees the
586+
// view won't be presented.
579587
Captures* captures = reinterpret_cast<Captures*>(result->user_data);
580588
captures->removed = result->removed;
581589
captures->latch.Signal();
582590
};
583591

584-
embedder_api_.RemoveView(engine_, &info);
592+
FlutterEngineResult result = embedder_api_.RemoveView(engine_, &info);
593+
if (result != kSuccess) {
594+
FML_LOG(ERROR) << "Starting the remove view operation failed. "
595+
"FlutterEngineRemoveView "
596+
"returned an unexpected result: "
597+
<< result
598+
<< ". This indicates a bug in the Windows embedder.";
599+
FML_DCHECK(false);
600+
return;
601+
}
585602

586603
// Block the platform thread until the engine has removed the view.
587604
// TODO(loicsharma): This blocks the platform thread eagerly and can

shell/platform/windows/flutter_windows_engine_unittests.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,5 +1265,53 @@ TEST_F(FlutterWindowsEngineTest, ReceivePlatformViewMessage) {
12651265
}
12661266
}
12671267

1268+
TEST_F(FlutterWindowsEngineTest, AddViewFailureDoesNotHang) {
1269+
FlutterWindowsEngineBuilder builder{GetContext()};
1270+
auto engine = builder.Build();
1271+
1272+
EngineModifier modifier{engine.get()};
1273+
1274+
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1275+
modifier.embedder_api().AddView = MOCK_ENGINE_PROC(
1276+
AddView,
1277+
[](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1278+
const FlutterAddViewInfo* info) { return kInternalInconsistency; });
1279+
1280+
ASSERT_TRUE(engine->Run());
1281+
1282+
// Create the first view. This is the implicit view and isn't added to the
1283+
// engine.
1284+
auto implicit_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1285+
1286+
std::unique_ptr<FlutterWindowsView> implicit_view =
1287+
engine->CreateView(std::move(implicit_window));
1288+
1289+
EXPECT_TRUE(implicit_view);
1290+
1291+
// Create a second view. The embedder attempts to add it to the engine.
1292+
auto second_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1293+
1294+
EXPECT_DEBUG_DEATH(engine->CreateView(std::move(second_window)),
1295+
"FlutterEngineAddView returned an unexpected result");
1296+
}
1297+
1298+
TEST_F(FlutterWindowsEngineTest, RemoveViewFailureDoesNotHang) {
1299+
FlutterWindowsEngineBuilder builder{GetContext()};
1300+
builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1301+
auto engine = builder.Build();
1302+
1303+
EngineModifier modifier{engine.get()};
1304+
1305+
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1306+
modifier.embedder_api().RemoveView = MOCK_ENGINE_PROC(
1307+
RemoveView,
1308+
[](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1309+
const FlutterRemoveViewInfo* info) { return kInternalInconsistency; });
1310+
1311+
ASSERT_TRUE(engine->Run());
1312+
EXPECT_DEBUG_DEATH(engine->RemoveView(123),
1313+
"FlutterEngineRemoveView returned an unexpected result");
1314+
}
1315+
12681316
} // namespace testing
12691317
} // namespace flutter

0 commit comments

Comments
 (0)