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

Commit 4ac21cb

Browse files
authored
Add completion callback to Shell::AddView (#51659)
In the future, `FlutterEngineAddView` will be added to the embedder API to allow embedders to add views. `FlutterEngineAddView` will accept a callback that notifies the embedder once the view has been added. This embedder API will be powered by `Shell::AddView`. This change adds a completion callback to `Shell::AddView` to prepare for the embedder API. Design doc: https://flutter.dev/go/multi-view-embedder-apis Part of flutter/flutter#144806 Part of flutter/flutter#142845 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent b77fc96 commit 4ac21cb

File tree

10 files changed

+386
-44
lines changed

10 files changed

+386
-44
lines changed

lib/ui/window/platform_configuration.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,18 @@ void PlatformConfiguration::DidCreateIsolate() {
8383
Dart_GetField(library, tonic::ToDart("_reportTimings")));
8484
}
8585

86-
void PlatformConfiguration::AddView(int64_t view_id,
86+
bool PlatformConfiguration::AddView(int64_t view_id,
8787
const ViewportMetrics& view_metrics) {
8888
auto [view_iterator, insertion_happened] =
8989
metrics_.emplace(view_id, view_metrics);
90-
FML_DCHECK(insertion_happened);
90+
if (!insertion_happened) {
91+
FML_LOG(ERROR) << "View #" << view_id << " already exists.";
92+
return false;
93+
}
9194

9295
std::shared_ptr<tonic::DartState> dart_state = add_view_.dart_state().lock();
9396
if (!dart_state) {
94-
return;
97+
return false;
9598
}
9699
tonic::DartState::Scope scope(dart_state);
97100
tonic::CheckAndHandleError(tonic::DartInvoke(
@@ -119,6 +122,7 @@ void PlatformConfiguration::AddView(int64_t view_id,
119122
tonic::ToDart(view_metrics.physical_display_features_state),
120123
tonic::ToDart(view_metrics.display_id),
121124
}));
125+
return true;
122126
}
123127

124128
bool PlatformConfiguration::RemoveView(int64_t view_id) {

lib/ui/window/platform_configuration.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,7 @@ class PlatformConfigurationClient {
262262
/// @brief A class for holding and distributing platform-level information
263263
/// to and from the Dart code in Flutter's framework.
264264
///
265-
/// It handles communication between the engine and the framework,
266-
/// and owns the main window.
265+
/// It handles communication between the engine and the framework.
267266
///
268267
/// It communicates with the RuntimeController through the use of a
269268
/// PlatformConfigurationClient interface, which the
@@ -315,7 +314,9 @@ class PlatformConfiguration final {
315314
/// @param[in] view_id The ID of the new view.
316315
/// @param[in] viewport_metrics The initial viewport metrics for the view.
317316
///
318-
void AddView(int64_t view_id, const ViewportMetrics& view_metrics);
317+
/// @return Whether the view was added.
318+
///
319+
bool AddView(int64_t view_id, const ViewportMetrics& view_metrics);
319320

320321
//----------------------------------------------------------------------------
321322
/// @brief Notify the framework that a view is no longer available.

runtime/runtime_controller.cc

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,30 @@ bool RuntimeController::FlushRuntimeStateToIsolate() {
121121
FML_DCHECK(!has_flushed_runtime_state_)
122122
<< "FlushRuntimeStateToIsolate is called more than once somehow.";
123123
has_flushed_runtime_state_ = true;
124+
125+
auto platform_configuration = GetPlatformConfigurationIfAvailable();
126+
if (!platform_configuration) {
127+
return false;
128+
}
129+
124130
for (auto const& [view_id, viewport_metrics] :
125131
platform_data_.viewport_metrics_for_views) {
126-
if (!AddView(view_id, viewport_metrics)) {
127-
return false;
132+
bool added = platform_configuration->AddView(view_id, viewport_metrics);
133+
134+
// Callbacks will have been already invoked if the engine was restarted.
135+
if (pending_add_view_callbacks_.find(view_id) !=
136+
pending_add_view_callbacks_.end()) {
137+
pending_add_view_callbacks_[view_id](added);
138+
pending_add_view_callbacks_.erase(view_id);
139+
}
140+
141+
if (!added) {
142+
FML_LOG(ERROR) << "Failed to flush view #" << view_id
143+
<< ". The Dart isolate may be in an inconsistent state.";
128144
}
129145
}
146+
147+
FML_DCHECK(pending_add_view_callbacks_.empty());
130148
return SetLocales(platform_data_.locale_data) &&
131149
SetSemanticsEnabled(platform_data_.semantics_enabled) &&
132150
SetAccessibilityFeatures(
@@ -136,25 +154,53 @@ bool RuntimeController::FlushRuntimeStateToIsolate() {
136154
SetDisplays(platform_data_.displays);
137155
}
138156

139-
bool RuntimeController::AddView(int64_t view_id,
140-
const ViewportMetrics& view_metrics) {
141-
platform_data_.viewport_metrics_for_views[view_id] = view_metrics;
142-
if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) {
143-
platform_configuration->AddView(view_id, view_metrics);
157+
void RuntimeController::AddView(int64_t view_id,
158+
const ViewportMetrics& view_metrics,
159+
AddViewCallback callback) {
160+
// If the Dart isolate is not running, |FlushRuntimeStateToIsolate| will
161+
// add the view and invoke the callback when the isolate is started.
162+
auto* platform_configuration = GetPlatformConfigurationIfAvailable();
163+
if (!platform_configuration) {
164+
FML_DCHECK(has_flushed_runtime_state_ == false);
165+
166+
if (pending_add_view_callbacks_.find(view_id) !=
167+
pending_add_view_callbacks_.end()) {
168+
FML_LOG(ERROR) << "View #" << view_id << " is already pending creation.";
169+
callback(false);
170+
return;
171+
}
144172

145-
return true;
173+
platform_data_.viewport_metrics_for_views[view_id] = view_metrics;
174+
pending_add_view_callbacks_[view_id] = std::move(callback);
175+
return;
146176
}
147177

148-
return false;
178+
FML_DCHECK(has_flushed_runtime_state_ || pending_add_view_callbacks_.empty());
179+
180+
platform_data_.viewport_metrics_for_views[view_id] = view_metrics;
181+
bool added = platform_configuration->AddView(view_id, view_metrics);
182+
callback(added);
149183
}
150184

151185
bool RuntimeController::RemoveView(int64_t view_id) {
152186
platform_data_.viewport_metrics_for_views.erase(view_id);
153-
if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) {
154-
return platform_configuration->RemoveView(view_id);
187+
188+
// If the Dart isolate has not been launched yet, the pending
189+
// add view operation's callback is stored by the runtime controller.
190+
// Notify this callback of the cancellation.
191+
auto* platform_configuration = GetPlatformConfigurationIfAvailable();
192+
if (!platform_configuration) {
193+
FML_DCHECK(has_flushed_runtime_state_ == false);
194+
if (pending_add_view_callbacks_.find(view_id) !=
195+
pending_add_view_callbacks_.end()) {
196+
pending_add_view_callbacks_[view_id](false);
197+
pending_add_view_callbacks_.erase(view_id);
198+
}
199+
200+
return false;
155201
}
156202

157-
return false;
203+
return platform_configuration->RemoveView(view_id);
158204
}
159205

160206
bool RuntimeController::SetViewportMetrics(int64_t view_id,

runtime/runtime_controller.h

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ class Window;
5050
///
5151
class RuntimeController : public PlatformConfigurationClient {
5252
public:
53+
/// A callback that's invoked after this `RuntimeController` attempts to
54+
/// add a view to the Dart isolate.
55+
///
56+
/// If the Dart isolate is not launched yet, this callback will be stored
57+
/// and invoked after the isolate is launched.
58+
///
59+
/// The `added` parameter is false if the add operation fails or was
60+
/// cancelled while pending using `RemoveView`.
61+
using AddViewCallback = std::function<void(bool added)>;
62+
5363
//----------------------------------------------------------------------------
5464
/// @brief Creates a new instance of a runtime controller. This is
5565
/// usually only done by the engine instance associated with the
@@ -174,23 +184,43 @@ class RuntimeController : public PlatformConfigurationClient {
174184
///
175185
/// A view must be added before other methods can refer to it,
176186
/// including the implicit view. Adding a view that already exists
177-
/// triggers an assertion.
187+
/// is an error.
188+
///
189+
/// The `callback` is invoked when the add operation is attempted,
190+
/// failed, or is cancelled.
191+
///
192+
/// If the isolate is not running, the view add will be queued and
193+
/// flushed to the isolate when it starts. Calling `RemoveView`
194+
/// before the isolate is launched cancels the add operation.
195+
///
178196
///
179197
/// @param[in] view_id The ID of the new view.
180198
/// @param[in] viewport_metrics The initial viewport metrics for the view.
199+
/// @param[in] callback Callback that will be invoked after the add
200+
/// operation is attempted or cancelled.
181201
///
182-
bool AddView(int64_t view_id, const ViewportMetrics& view_metrics);
202+
void AddView(int64_t view_id,
203+
const ViewportMetrics& view_metrics,
204+
AddViewCallback callback);
183205

184206
//----------------------------------------------------------------------------
185207
/// @brief Notify the isolate that a view is no longer available.
186208
///
187-
/// Removing a view that does not exist triggers an assertion.
209+
/// Views that are added before the isolate is started are
210+
/// queued until the isolate is launched. If one of these
211+
/// "pending" views are removed, the view add is cancelled:
212+
/// the `AddViewCallback` will be invoked with an `added` of
213+
/// false and `RemoveView` will return false.
188214
///
189215
/// The implicit view (kFlutterImplicitViewId) should never be
190216
/// removed. Doing so triggers an assertion.
191217
///
192218
/// @param[in] view_id The ID of the view.
193219
///
220+
/// @return If the remove view operation was forwarded to the running
221+
/// isolate. False if the view does not exist. If the Dart isolate
222+
/// is not running, then the pending view creation (if any) is
223+
/// cancelled and the return value is always false.
194224
bool RemoveView(int64_t view_id);
195225

196226
//----------------------------------------------------------------------------
@@ -660,6 +690,12 @@ class RuntimeController : public PlatformConfigurationClient {
660690
std::shared_ptr<PlatformIsolateManager>(new PlatformIsolateManager());
661691
bool has_flushed_runtime_state_ = false;
662692

693+
// Callbacks when `AddView` was called before the Dart isolate is launched.
694+
//
695+
// These views will be added when `FlushRuntimeStateToIsolate` is called.
696+
// This is no longer used once the Dart isolate starts.
697+
std::unordered_map<int64_t, AddViewCallback> pending_add_view_callbacks_;
698+
663699
// Tracks the views that have been called `Render` during a frame.
664700
//
665701
// If all views that have been registered by `AddView` have been called

shell/common/engine.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,10 @@ tonic::DartErrorHandleType Engine::GetUIIsolateLastError() {
297297
return runtime_controller_->GetLastError();
298298
}
299299

300-
void Engine::AddView(int64_t view_id, const ViewportMetrics& view_metrics) {
301-
runtime_controller_->AddView(view_id, view_metrics);
300+
void Engine::AddView(int64_t view_id,
301+
const ViewportMetrics& view_metrics,
302+
std::function<void(bool added)> callback) {
303+
runtime_controller_->AddView(view_id, view_metrics, std::move(callback));
302304
}
303305

304306
bool Engine::RemoveView(int64_t view_id) {

shell/common/engine.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,8 +721,12 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
721721
///
722722
/// @param[in] view_id The ID of the new view.
723723
/// @param[in] viewport_metrics The initial viewport metrics for the view.
724+
/// @param[in] callback Callback that will be invoked once
725+
/// the engine attempts to add the view.
724726
///
725-
void AddView(int64_t view_id, const ViewportMetrics& view_metrics);
727+
void AddView(int64_t view_id,
728+
const ViewportMetrics& view_metrics,
729+
std::function<void(bool added)> callback);
726730

727731
//----------------------------------------------------------------------------
728732
/// @brief Notify the Flutter application that a view is no

shell/common/engine_animator_unittests.cc

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,10 @@ TEST_F(EngineAnimatorTest, AnimatorAcceptsMultipleRenders) {
313313
engine_context->Run(std::move(configuration));
314314

315315
engine_context->EngineTaskSync([](Engine& engine) {
316-
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0});
317-
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0});
316+
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0},
317+
[](bool added) { ASSERT_TRUE(added); });
318+
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0},
319+
[](bool added) { ASSERT_TRUE(added); });
318320
});
319321

320322
native_latch.Wait();
@@ -368,8 +370,10 @@ TEST_F(EngineAnimatorTest, IgnoresOutOfFrameRenders) {
368370
std::move(animator));
369371

370372
engine_context->EngineTaskSync([](Engine& engine) {
371-
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0});
372-
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0});
373+
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0},
374+
[](bool added) { ASSERT_TRUE(added); });
375+
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0},
376+
[](bool added) { ASSERT_TRUE(added); });
373377
});
374378

375379
auto configuration = RunConfiguration::InferFromSettings(settings_);
@@ -444,7 +448,8 @@ TEST_F(EngineAnimatorTest, IgnoresDuplicateRenders) {
444448
std::move(animator));
445449

446450
engine_context->EngineTaskSync([](Engine& engine) {
447-
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1, 10, 10, 22, 0});
451+
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1, 10, 10, 22, 0},
452+
[](bool added) { ASSERT_TRUE(added); });
448453
});
449454

450455
auto configuration = RunConfiguration::InferFromSettings(settings_);
@@ -504,7 +509,8 @@ TEST_F(EngineAnimatorTest, AnimatorSubmitsImplicitViewBeforeDrawFrameEnds) {
504509
std::move(animator));
505510

506511
engine_context->EngineTaskSync([](Engine& engine) {
507-
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0});
512+
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0},
513+
[](bool added) { ASSERT_TRUE(added); });
508514
});
509515

510516
auto configuration = RunConfiguration::InferFromSettings(settings_);
@@ -568,7 +574,8 @@ TEST_F(EngineAnimatorTest, AnimatorSubmitWarmUpImplicitView) {
568574
engine.ScheduleFrame(true);
569575
// Add the implicit view so that the engine recognizes it and that its
570576
// metrics is not empty.
571-
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0});
577+
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0},
578+
[](bool added) { ASSERT_TRUE(added); });
572579
});
573580
continuation_ready_latch.Wait();
574581

@@ -634,9 +641,12 @@ TEST_F(EngineAnimatorTest, AnimatorSubmitPartialViewsForWarmUp) {
634641
// Schedule a frame to make the animator create a continuation.
635642
engine.ScheduleFrame(true);
636643
// Add multiple views.
637-
engine.AddView(0, ViewportMetrics{1, 10, 10, 22, 0});
638-
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0});
639-
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0});
644+
engine.AddView(0, ViewportMetrics{1, 10, 10, 22, 0},
645+
[](bool added) { ASSERT_TRUE(added); });
646+
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0},
647+
[](bool added) { ASSERT_TRUE(added); });
648+
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0},
649+
[](bool added) { ASSERT_TRUE(added); });
640650
});
641651

642652
continuation_ready_latch.Wait();

shell/common/shell.cc

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,11 @@ bool Shell::Setup(std::unique_ptr<PlatformView> platform_view,
754754
weak_rasterizer_ = rasterizer_->GetWeakPtr();
755755
weak_platform_view_ = platform_view_->GetWeakPtr();
756756

757-
engine_->AddView(kFlutterImplicitViewId, ViewportMetrics{});
757+
// Add the implicit view with empty metrics.
758+
engine_->AddView(kFlutterImplicitViewId, ViewportMetrics{}, [](bool added) {
759+
FML_DCHECK(added) << "Failed to add the implicit view";
760+
});
761+
758762
// Setup the time-consuming default font manager right after engine created.
759763
if (!settings_.prefetched_default_font_manager) {
760764
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
@@ -2102,7 +2106,9 @@ bool Shell::OnServiceProtocolReloadAssetFonts(
21022106
return true;
21032107
}
21042108

2105-
void Shell::AddView(int64_t view_id, const ViewportMetrics& viewport_metrics) {
2109+
void Shell::AddView(int64_t view_id,
2110+
const ViewportMetrics& viewport_metrics,
2111+
AddViewCallback callback) {
21062112
TRACE_EVENT0("flutter", "Shell::AddView");
21072113
FML_DCHECK(is_set_up_);
21082114
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
@@ -2112,10 +2118,11 @@ void Shell::AddView(int64_t view_id, const ViewportMetrics& viewport_metrics) {
21122118

21132119
task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr(), //
21142120
viewport_metrics, //
2115-
view_id //
2121+
view_id, //
2122+
callback = std::move(callback) //
21162123
] {
21172124
if (engine) {
2118-
engine->AddView(view_id, viewport_metrics);
2125+
engine->AddView(view_id, viewport_metrics, callback);
21192126
}
21202127
});
21212128
}

shell/common/shell.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ class Shell final : public PlatformView::Delegate,
133133
const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch,
134134
impeller::RuntimeStageBackend runtime_stage_type)>
135135
EngineCreateCallback;
136+
using AddViewCallback = std::function<void(bool added)>;
136137
using RemoveViewCallback = std::function<void(bool removed)>;
137138

138139
//----------------------------------------------------------------------------
@@ -317,8 +318,12 @@ class Shell final : public PlatformView::Delegate,
317318
///
318319
/// @param[in] view_id The view ID of the new view.
319320
/// @param[in] viewport_metrics The initial viewport metrics for the view.
321+
/// @param[in] callback The callback that's invoked once the engine
322+
/// has attempted to add the view.
320323
///
321-
void AddView(int64_t view_id, const ViewportMetrics& viewport_metrics);
324+
void AddView(int64_t view_id,
325+
const ViewportMetrics& viewport_metrics,
326+
AddViewCallback callback);
322327

323328
/// @brief Deallocates resources for a non-implicit view.
324329
///

0 commit comments

Comments
 (0)