diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index d48442604ff5c..cd3fada30afb4 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -8,8 +8,6 @@ #include "flutter/fml/make_copyable.h" #include "flutter/fml/synchronization/waitable_event.h" -#include "flutter/shell/common/rasterizer.h" -#include "flutter/shell/common/shell.h" #include "flutter/shell/common/vsync_waiter_fallback.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 1cbf73ce4abea..48f4648d665ac 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -29,8 +29,6 @@ namespace flutter { -class Shell; - //------------------------------------------------------------------------------ /// @brief Platform views are created by the shell on the platform task /// runner. Unless explicitly specified, all platform view methods diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index e3aae4df40c4a..dfe40017dfb41 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1168,6 +1168,14 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, }; } + flutter::PlatformViewEmbedder::OnPreEngineRestartCallback + on_pre_engine_restart_callback = nullptr; + if (SAFE_ACCESS(args, on_pre_engine_restart_callback, nullptr) != nullptr) { + on_pre_engine_restart_callback = [ptr = + args->on_pre_engine_restart_callback, + user_data]() { return ptr(user_data); }; + } + auto external_view_embedder_result = InferExternalViewEmbedderFromArgs(SAFE_ACCESS(args, compositor, nullptr)); if (external_view_embedder_result.second) { @@ -1182,6 +1190,7 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, platform_message_response_callback, // vsync_callback, // compute_platform_resolved_locale_callback, // + on_pre_engine_restart_callback, // }; auto on_create_platform_view = InferPlatformViewCreationCallback( diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 0a63b5580e680..d12d2a5a396fe 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -313,6 +313,7 @@ typedef bool (*TextureFrameCallback)(void* /* user data */, size_t /* height */, FlutterOpenGLTexture* /* texture out */); typedef void (*VsyncCallback)(void* /* user data */, intptr_t /* baton */); +typedef void (*OnPreEngineRestartCallback)(void* /* user data */); /// A structure to represent the width and height. typedef struct { @@ -1577,6 +1578,16 @@ typedef struct { // or component name to embedder's logger. This string will be passed to to // callbacks on `log_message_callback`. Defaults to "flutter" if unspecified. const char* log_tag; + + // A callback that is invoked when the engine is restarted. + // + // This optional callback is typically used to reset states to as if the + // engine has just been started, and usually indicates the user has requested + // a hot restart (Shift-R in the Flutter CLI.) It is not called the first time + // the engine starts. + // + // The first argument is the `user_data` from `FlutterEngineInitialize`. + OnPreEngineRestartCallback on_pre_engine_restart_callback; } FlutterProjectArgs; #ifndef FLUTTER_ENGINE_NO_PROTOTYPES diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index 2cd020719944d..2a2752446f5e2 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -130,4 +130,11 @@ PlatformViewEmbedder::ComputePlatformResolvedLocales( return out; } +// |PlatformView| +void PlatformViewEmbedder::OnPreEngineRestart() const { + if (platform_dispatch_table_.on_pre_engine_restart_callback != nullptr) { + platform_dispatch_table_.on_pre_engine_restart_callback(); + } +} + } // namespace flutter diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index 3ecc634b46b71..22f4d2dee6ea0 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -36,6 +36,7 @@ class PlatformViewEmbedder final : public PlatformView { using ComputePlatformResolvedLocaleCallback = std::function>( const std::vector& supported_locale_data)>; + using OnPreEngineRestartCallback = std::function; struct PlatformDispatchTable { UpdateSemanticsNodesCallback update_semantics_nodes_callback; // optional @@ -46,6 +47,7 @@ class PlatformViewEmbedder final : public PlatformView { VsyncWaiterEmbedder::VsyncCallback vsync_callback; // optional ComputePlatformResolvedLocaleCallback compute_platform_resolved_locale_callback; + OnPreEngineRestartCallback on_pre_engine_restart_callback; // optional }; // Create a platform view that sets up a software rasterizer. @@ -104,6 +106,9 @@ class PlatformViewEmbedder final : public PlatformView { // |PlatformView| std::unique_ptr CreateVSyncWaiter() override; + // |PlatformView| + void OnPreEngineRestart() const override; + // |PlatformView| std::unique_ptr> ComputePlatformResolvedLocales( const std::vector& supported_locale_data) override; diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 05e3d084e3e9f..0033a3195dd17 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -47,6 +47,11 @@ struct _FlEngine { FlEngineUpdateSemanticsNodeHandler update_semantics_node_handler; gpointer update_semantics_node_handler_data; GDestroyNotify update_semantics_node_handler_destroy_notify; + + // Function to call when the engine is restarted. + FlEngineOnPreEngineRestartHandler on_pre_engine_restart_handler; + gpointer on_pre_engine_restart_handler_data; + GDestroyNotify on_pre_engine_restart_handler_destroy_notify; }; G_DEFINE_QUARK(fl_engine_error_quark, fl_engine_error) @@ -283,6 +288,20 @@ static void fl_engine_update_semantics_node_cb(const FlutterSemanticsNode* node, } } +// Called when the engine is restarted. +// +// This method should reset states to as if the engine has just been started, +// which usually indicates the user has requested a hot restart (Shift-R in the +// Flutter CLI.) +static void fl_engine_on_pre_engine_restart_cb(void* user_data) { + FlEngine* self = FL_ENGINE(user_data); + + if (self->on_pre_engine_restart_handler != nullptr) { + self->on_pre_engine_restart_handler( + self, self->on_pre_engine_restart_handler_data); + } +} + // Called when a response to a sent platform message is received from the // engine. static void fl_engine_platform_message_response_cb(const uint8_t* data, @@ -342,6 +361,13 @@ static void fl_engine_dispose(GObject* object) { self->update_semantics_node_handler_data = nullptr; self->update_semantics_node_handler_destroy_notify = nullptr; + if (self->on_pre_engine_restart_handler_destroy_notify) { + self->on_pre_engine_restart_handler_destroy_notify( + self->on_pre_engine_restart_handler_data); + } + self->on_pre_engine_restart_handler_data = nullptr; + self->on_pre_engine_restart_handler_destroy_notify = nullptr; + G_OBJECT_CLASS(fl_engine_parent_class)->dispose(object); } @@ -425,6 +451,7 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { args.update_semantics_node_callback = fl_engine_update_semantics_node_cb; args.custom_task_runners = &custom_task_runners; args.shutdown_dart_vm_when_done = true; + args.on_pre_engine_restart_callback = fl_engine_on_pre_engine_restart_cb; args.dart_entrypoint_argc = dart_entrypoint_args != nullptr ? g_strv_length(dart_entrypoint_args) : 0; args.dart_entrypoint_argv = @@ -519,6 +546,23 @@ void fl_engine_set_update_semantics_node_handler( self->update_semantics_node_handler_destroy_notify = destroy_notify; } +void fl_engine_set_on_pre_engine_restart_handler( + FlEngine* self, + FlEngineOnPreEngineRestartHandler handler, + gpointer user_data, + GDestroyNotify destroy_notify) { + g_return_if_fail(FL_IS_ENGINE(self)); + + if (self->on_pre_engine_restart_handler_destroy_notify) { + self->on_pre_engine_restart_handler_destroy_notify( + self->on_pre_engine_restart_handler_data); + } + + self->on_pre_engine_restart_handler = handler; + self->on_pre_engine_restart_handler_data = user_data; + self->on_pre_engine_restart_handler_destroy_notify = destroy_notify; +} + gboolean fl_engine_send_platform_message_response( FlEngine* self, const FlutterPlatformMessageResponseHandle* handle, diff --git a/shell/platform/linux/fl_engine_private.h b/shell/platform/linux/fl_engine_private.h index fdc35ac54b5c4..a576a64e9608c 100644 --- a/shell/platform/linux/fl_engine_private.h +++ b/shell/platform/linux/fl_engine_private.h @@ -58,6 +58,16 @@ typedef void (*FlEngineUpdateSemanticsNodeHandler)( const FlutterSemanticsNode* node, gpointer user_data); +/** + * FlEngineOnPreEngineRestartHandler: + * @engine: an #FlEngine. + * @user_data: semantic node information. + * + * @user_data: (closure): data provided when registering this handler. + */ +typedef void (*FlEngineOnPreEngineRestartHandler)(FlEngine* engine, + gpointer user_data); + /** * fl_engine_new: * @project: an #FlDartProject. @@ -115,6 +125,22 @@ void fl_engine_set_update_semantics_node_handler( gpointer user_data, GDestroyNotify destroy_notify); +/** + * fl_engine_set_on_pre_engine_restart_handler: + * @engine: an #FlEngine. + * @handler: function to call when the engine is restarted. + * @user_data: (closure): user data to pass to @handler. + * @destroy_notify: (allow-none): a function which gets called to free + * @user_data, or %NULL. + * + * Registers the function called when the engine is restarted. + */ +void fl_engine_set_on_pre_engine_restart_handler( + FlEngine* engine, + FlEngineOnPreEngineRestartHandler handler, + gpointer user_data, + GDestroyNotify destroy_notify); + /** * fl_engine_start: * @engine: an #FlEngine. diff --git a/shell/platform/linux/fl_engine_test.cc b/shell/platform/linux/fl_engine_test.cc index 688e2e6883cc4..082f936ee8fe7 100644 --- a/shell/platform/linux/fl_engine_test.cc +++ b/shell/platform/linux/fl_engine_test.cc @@ -225,6 +225,62 @@ TEST(FlEngineTest, SettingsPlugin) { EXPECT_TRUE(called); } +void on_pre_engine_restart_cb(FlEngine* engine, gpointer user_data) { + int* count = reinterpret_cast(user_data); + *count += 1; +} + +void on_pre_engine_restart_destroy_notify(gpointer user_data) { + int* count = reinterpret_cast(user_data); + *count += 10; +} + +// Checks restarting the engine invokes the correct callback. +TEST(FlEngineTest, OnPreEngineRestart) { + g_autoptr(FlEngine) engine = make_mock_engine(); + FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine); + + OnPreEngineRestartCallback callback; + void* callback_user_data; + + bool called = false; + embedder_api->Initialize = MOCK_ENGINE_PROC( + Initialize, ([&callback, &callback_user_data, &called]( + size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + called = true; + callback = args->on_pre_engine_restart_callback; + callback_user_data = user_data; + + return kSuccess; + })); + + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + EXPECT_EQ(error, nullptr); + + EXPECT_TRUE(called); + EXPECT_NE(callback, nullptr); + + // The following call has no effect but should not crash. + callback(callback_user_data); + + int count = 0; + + // Set a handler, and the call should has an effect. + fl_engine_set_on_pre_engine_restart_handler( + engine, on_pre_engine_restart_cb, &count, + on_pre_engine_restart_destroy_notify); + + callback(callback_user_data); + EXPECT_EQ(count, 1); + + // Disposal should call the destroy notify. + g_object_unref(engine); + EXPECT_EQ(count, 11); +} + TEST(FlEngineTest, DartEntrypointArgs) { g_autoptr(FlDartProject) project = fl_dart_project_new();