diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 0377cc91e839d..7bba32b761a83 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #define RAPIDJSON_HAS_STDSTRING 1 - #include "flutter/shell/common/shell.h" #include @@ -372,6 +371,79 @@ void Shell::NotifyLowMemoryWarning() const { // to purge them. } +void Shell::RunEngine(RunConfiguration run_configuration) { + RunEngine(std::move(run_configuration), nullptr); +} + +void Shell::RunEngine(RunConfiguration run_configuration, + std::function result_callback) { + auto result = [platform_runner = task_runners_.GetPlatformTaskRunner(), + result_callback](Engine::RunStatus run_result) { + if (!result_callback) { + return; + } + platform_runner->PostTask( + [result_callback, run_result]() { result_callback(run_result); }); + }; + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + if (!weak_engine_) { + result(Engine::RunStatus::Failure); + } + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), + fml::MakeCopyable( + [run_configuration = std::move(run_configuration), + weak_engine = weak_engine_, result]() mutable { + if (!weak_engine) { + FML_LOG(ERROR) + << "Could not launch engine with configuration - no engine."; + result(Engine::RunStatus::Failure); + return; + } + auto run_result = weak_engine->Run(std::move(run_configuration)); + if (run_result == flutter::Engine::RunStatus::Failure) { + FML_LOG(ERROR) << "Could not launch engine with configuration."; + } + result(run_result); + })); +} + +std::optional Shell::GetUIIsolateLastError() const { + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + // We're using the unique_ptr here because we're sure we're on the Platform + // Thread and callers expect this to be synchronous. + if (!engine_) { + return std::nullopt; + } + switch (engine_->GetUIIsolateLastError()) { + case tonic::kCompilationErrorType: + return DartErrorCode::CompilationError; + case tonic::kApiErrorType: + return DartErrorCode::ApiError; + case tonic::kUnknownErrorType: + return DartErrorCode::UnknownError; + case tonic::kNoError: + return DartErrorCode::NoError; + } + return DartErrorCode::UnknownError; +} + +bool Shell::EngineHasLivePorts() const { + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + // We're using the unique_ptr here because we're sure we're on the Platform + // Thread and callers expect this to be synchronous. + if (!engine_) { + return false; + } + return engine_->UIIsolateHasLivePorts(); +} + bool Shell::IsSetup() const { return is_setup_; } @@ -425,10 +497,14 @@ fml::WeakPtr Shell::GetRasterizer() { return weak_rasterizer_; } +// TODO(dnfield): Remove this when either Topaz is up to date or flutter_runner +// is built out of this repo. +#ifdef OS_FUCHSIA fml::WeakPtr Shell::GetEngine() { FML_DCHECK(is_setup_); return weak_engine_; } +#endif // OS_FUCHSIA fml::WeakPtr Shell::GetPlatformView() { FML_DCHECK(is_setup_); @@ -907,7 +983,7 @@ void Shell::ReportTimings() { auto timings = std::move(unreported_timings_); unreported_timings_ = {}; - task_runners_.GetUITaskRunner()->PostTask([timings, engine = GetEngine()] { + task_runners_.GetUITaskRunner()->PostTask([timings, engine = weak_engine_] { if (engine) { engine->ReportTimings(std::move(timings)); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 2ac4a1b177048..4f635e3618ad8 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -35,6 +35,18 @@ namespace flutter { +/// Error exit codes for the Dart isolate. +enum class DartErrorCode { + /// No error has occurred. + NoError = 0, + /// The Dart error code for an API error. + ApiError = 253, + /// The Dart error code for a compilation error. + CompilationError = 254, + /// The Dart error code for an unkonwn error. + UnknownError = 255 +}; + //------------------------------------------------------------------------------ /// Perhaps the single most important class in the Flutter engine repository. /// When embedders create a Flutter application, they are referring to the @@ -165,6 +177,19 @@ class Shell final : public PlatformView::Delegate, /// ~Shell(); + //---------------------------------------------------------------------------- + /// @brief Starts an isolate for the given RunConfiguration. + /// + void RunEngine(RunConfiguration run_configuration); + + //---------------------------------------------------------------------------- + /// @brief Starts an isolate for the given RunConfiguration. The + /// result_callback will be called with the status of the + /// operation. + /// + void RunEngine(RunConfiguration run_configuration, + std::function result_callback); + //------------------------------------------------------------------------------ /// @return The settings used to launch this shell. /// @@ -191,12 +216,18 @@ class Shell final : public PlatformView::Delegate, /// fml::WeakPtr GetRasterizer(); +// TODO(dnfield): Remove this when either Topaz is up to date or flutter_runner +// is built out of this repo. +#ifdef OS_FUCHSIA //------------------------------------------------------------------------------ - /// @brief Engines may only be accessed on the UI thread. + /// @brief Engines may only be accessed on the UI thread. This method is + /// deprecated, and implementers should instead use other API + /// available on the Shell or the PlatformView. /// /// @return A weak pointer to the engine. /// fml::WeakPtr GetEngine(); +#endif // OS_FUCHSIA //---------------------------------------------------------------------------- /// @brief Platform views may only be accessed on the platform task @@ -253,6 +284,25 @@ class Shell final : public PlatformView::Delegate, /// fml::Status WaitForFirstFrame(fml::TimeDelta timeout); + //---------------------------------------------------------------------------- + /// @brief Used by embedders to get the last error from the Dart UI + /// Isolate, if one exists. + /// + /// @return Returns the last error code from the UI Isolate. + /// + std::optional GetUIIsolateLastError() const; + + //---------------------------------------------------------------------------- + /// @brief Used by embedders to check if the Engine is running and has + /// any live ports remaining. For example, the Flutter tester uses + /// this method to check whether it should continue to wait for + /// a running test or not. + /// + /// @return Returns if the shell has an engine and the engine has any live + /// Dart ports. + /// + bool EngineHasLivePorts() const; + private: using ServiceProtocolHandler = std::functionGetTaskRunners().GetUITaskRunner(), - fml::MakeCopyable([&latch, config = std::move(configuration), - engine = shell->GetEngine()]() mutable { - ASSERT_TRUE(engine); - ASSERT_EQ(engine->Run(std::move(config)), Engine::RunStatus::Success); - latch.Signal(); - })); + shell->GetTaskRunners().GetPlatformTaskRunner(), + [shell, &latch, &configuration]() { + shell->RunEngine(std::move(configuration), + [&latch](Engine::RunStatus run_status) { + ASSERT_EQ(run_status, Engine::RunStatus::Success); + latch.Signal(); + }); + }); latch.Wait(); } @@ -91,7 +92,7 @@ void ShellTest::PumpOneFrame(Shell* shell) { // won't be rasterized. fml::AutoResetWaitableEvent latch; shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [&latch, engine = shell->GetEngine()]() { + [&latch, engine = shell->weak_engine_]() { engine->SetViewportMetrics({1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}); engine->animator_->BeginFrame(fml::TimePoint::Now(), fml::TimePoint::Now()); @@ -101,7 +102,7 @@ void ShellTest::PumpOneFrame(Shell* shell) { latch.Reset(); // Call |Render| to rasterize a layer tree and trigger |OnFrameRasterized| - fml::WeakPtr runtime_delegate = shell->GetEngine(); + fml::WeakPtr runtime_delegate = shell->weak_engine_; shell->GetTaskRunners().GetUITaskRunner()->PostTask( [&latch, runtime_delegate]() { auto layer_tree = std::make_unique(); diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index e9b114fc42252..452e069bd5276 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -157,53 +157,7 @@ void AndroidShellHolder::Launch(RunConfiguration config) { return; } - shell_->GetTaskRunners().GetUITaskRunner()->PostTask( - fml::MakeCopyable([engine = shell_->GetEngine(), // - config = std::move(config) // - ]() mutable { - FML_LOG(INFO) << "Attempting to launch engine configuration..."; - if (!engine || - engine->Run(std::move(config)) == Engine::RunStatus::Failure) { - FML_LOG(ERROR) << "Could not launch engine in configuration."; - } else { - FML_LOG(INFO) << "Isolate for engine configuration successfully " - "started and run."; - } - })); -} - -void AndroidShellHolder::SetViewportMetrics( - const flutter::ViewportMetrics& metrics) { - if (!IsValid()) { - return; - } - - shell_->GetTaskRunners().GetUITaskRunner()->PostTask( - [engine = shell_->GetEngine(), metrics]() { - if (engine) { - engine->SetViewportMetrics(metrics); - } - }); -} - -void AndroidShellHolder::DispatchPointerDataPacket( - std::unique_ptr packet) { - if (!IsValid()) { - return; - } - - TRACE_EVENT0("flutter", "AndroidShellHolder::DispatchPointerDataPacket"); - TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_); - - shell_->GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = shell_->GetEngine(), packet = std::move(packet), - flow_id = next_pointer_flow_id_] { - if (engine) { - engine->DispatchPointerDataPacket(*packet, flow_id); - } - })); - - next_pointer_flow_id_++; + shell_->RunEngine(std::move(config)); } Rasterizer::Screenshot AndroidShellHolder::Screenshot( diff --git a/shell/platform/android/android_shell_holder.h b/shell/platform/android/android_shell_holder.h index ce966ebb6da66..83d92b77886b3 100644 --- a/shell/platform/android/android_shell_holder.h +++ b/shell/platform/android/android_shell_holder.h @@ -30,11 +30,6 @@ class AndroidShellHolder { void Launch(RunConfiguration configuration); - void SetViewportMetrics(const flutter::ViewportMetrics& metrics); - - void DispatchPointerDataPacket( - std::unique_ptr packet); - const flutter::Settings& GetSettings() const; fml::WeakPtr GetPlatformView(); diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index eaeb349d5ef8a..f25ddefb9becc 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -276,7 +276,7 @@ static void SetViewportMetrics(JNIEnv* env, static_cast(physicalViewInsetLeft), }; - ANDROID_SHELL_HOLDER->SetViewportMetrics(metrics); + ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics(metrics); } static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { @@ -387,7 +387,8 @@ static void DispatchPointerDataPacket(JNIEnv* env, jint position) { uint8_t* data = static_cast(env->GetDirectBufferAddress(buffer)); auto packet = std::make_unique(data, position); - ANDROID_SHELL_HOLDER->DispatchPointerDataPacket(std::move(packet)); + ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchPointerDataPacket( + std::move(packet)); } static void DispatchSemanticsAction(JNIEnv* env, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index ccf9a26dc50f6..54e31b8700a11 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -63,8 +63,6 @@ @implementation FlutterEngine { int64_t _nextTextureId; - uint64_t _nextPointerFlowId; - BOOL _allowHeadlessExecution; FlutterBinaryMessengerRelay* _binaryMessenger; } @@ -126,24 +124,17 @@ - (void)dealloc { } - (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics { - self.shell.GetTaskRunners().GetUITaskRunner()->PostTask( - [engine = self.shell.GetEngine(), metrics = viewportMetrics]() { - if (engine) { - engine->SetViewportMetrics(std::move(metrics)); - } - }); + if (!self.platformView) { + return; + } + self.platformView->SetViewportMetrics(std::move(viewportMetrics)); } - (void)dispatchPointerDataPacket:(std::unique_ptr)packet { - TRACE_EVENT0("flutter", "dispatchPointerDataPacket"); - TRACE_FLOW_BEGIN("flutter", "PointerEvent", _nextPointerFlowId); - self.shell.GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = self.shell.GetEngine(), packet = std::move(packet), flow_id = _nextPointerFlowId] { - if (engine) { - engine->DispatchPointerDataPacket(*packet, flow_id); - } - })); - _nextPointerFlowId++; + if (!self.platformView) { + return; + } + self.platformView->DispatchPointerDataPacket(std::move(packet)); } - (fml::WeakPtr)platformView { @@ -312,18 +303,8 @@ - (void)maybeSetupPlatformViewChannels { - (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil { // Launch the Dart application with the inferred run configuration. - self.shell.GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = _shell->GetEngine(), - config = [_dartProject.get() runConfigurationForEntrypoint:entrypoint - libraryOrNil:libraryOrNil] // - ]() mutable { - if (engine) { - auto result = engine->Run(std::move(config)); - if (result == flutter::Engine::RunStatus::Failure) { - FML_LOG(ERROR) << "Could not launch engine with configuration."; - } - } - })); + self.shell.RunEngine([_dartProject.get() runConfigurationForEntrypoint:entrypoint + libraryOrNil:libraryOrNil]); } - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 1dd109bfd53eb..6f675e785dabb 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -63,19 +63,7 @@ bool EmbedderEngine::Run(RunConfiguration run_configuration) { if (!IsValid() || !run_configuration.IsValid()) { return false; } - - shell_->GetTaskRunners().GetUITaskRunner()->PostTask( - fml::MakeCopyable([engine = shell_->GetEngine(), // engine - config = std::move(run_configuration) // config - ]() mutable { - if (engine) { - auto result = engine->Run(std::move(config)); - if (result == Engine::RunStatus::Failure) { - FML_LOG(ERROR) << "Could not launch the engine with configuration."; - } - } - })); - + shell_->RunEngine(std::move(run_configuration)); return true; } @@ -84,12 +72,11 @@ bool EmbedderEngine::SetViewportMetrics(flutter::ViewportMetrics metrics) { return false; } - shell_->GetTaskRunners().GetUITaskRunner()->PostTask( - [engine = shell_->GetEngine(), metrics = std::move(metrics)]() { - if (engine) { - engine->SetViewportMetrics(std::move(metrics)); - } - }); + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + platform_view->SetViewportMetrics(std::move(metrics)); return true; } @@ -99,18 +86,12 @@ bool EmbedderEngine::DispatchPointerDataPacket( return false; } - TRACE_EVENT0("flutter", "EmbedderEngine::DispatchPointerDataPacket"); - TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_); - - shell_->GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = shell_->GetEngine(), packet = std::move(packet), - flow_id = next_pointer_flow_id_] { - if (engine) { - engine->DispatchPointerDataPacket(*packet, flow_id); - } - })); - next_pointer_flow_id_++; + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + platform_view->DispatchPointerDataPacket(std::move(packet)); return true; } @@ -120,13 +101,12 @@ bool EmbedderEngine::SendPlatformMessage( return false; } - shell_->GetTaskRunners().GetUITaskRunner()->PostTask( - [engine = shell_->GetEngine(), message] { - if (engine) { - engine->DispatchPlatformMessage(message); - } - }); + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + platform_view->DispatchPlatformMessage(message); return true; } @@ -160,12 +140,12 @@ bool EmbedderEngine::SetSemanticsEnabled(bool enabled) { if (!IsValid()) { return false; } - shell_->GetTaskRunners().GetUITaskRunner()->PostTask( - [engine = shell_->GetEngine(), enabled] { - if (engine) { - engine->SetSemanticsEnabled(enabled); - } - }); + + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + platform_view->SetSemanticsEnabled(enabled); return true; } @@ -173,12 +153,11 @@ bool EmbedderEngine::SetAccessibilityFeatures(int32_t flags) { if (!IsValid()) { return false; } - shell_->GetTaskRunners().GetUITaskRunner()->PostTask( - [engine = shell_->GetEngine(), flags] { - if (engine) { - engine->SetAccessibilityFeatures(flags); - } - }); + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + platform_view->SetAccessibilityFeatures(flags); return true; } @@ -188,16 +167,11 @@ bool EmbedderEngine::DispatchSemanticsAction(int id, if (!IsValid()) { return false; } - shell_->GetTaskRunners().GetUITaskRunner()->PostTask( - fml::MakeCopyable([engine = shell_->GetEngine(), // engine - id, // id - action, // action - args = std::move(args) // args - ]() mutable { - if (engine) { - engine->DispatchSemanticsAction(id, action, std::move(args)); - } - })); + auto platform_view = shell_->GetPlatformView(); + if (!platform_view) { + return false; + } + platform_view->DispatchSemanticsAction(id, action, std::move(args)); return true; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index dfae111ba6e81..8ef64502f98cc 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -78,7 +78,6 @@ class EmbedderEngine { const EmbedderExternalTextureGL::ExternalTextureCallback external_texture_callback_; bool is_valid_ = false; - uint64_t next_pointer_flow_id_ = 0; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderEngine); }; diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 2acfae056f354..52f6b15590f6c 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -20,6 +20,7 @@ #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" #include "third_party/dart/runtime/include/bin/dart_io_api.h" +#include "third_party/dart/runtime/include/dart_api.h" namespace flutter { @@ -30,35 +31,20 @@ class ScriptCompletionTaskObserver { ScriptCompletionTaskObserver(Shell& shell, fml::RefPtr main_task_runner, bool run_forever) - : engine_(shell.GetEngine()), + : shell_(shell), main_task_runner_(std::move(main_task_runner)), run_forever_(run_forever) {} int GetExitCodeForLastError() const { - // Exit codes used by the Dart command line tool. - const int kApiErrorExitCode = 253; - const int kCompilationErrorExitCode = 254; - const int kErrorExitCode = 255; - switch (last_error_) { - case tonic::kCompilationErrorType: - return kCompilationErrorExitCode; - case tonic::kApiErrorType: - return kApiErrorExitCode; - case tonic::kUnknownErrorType: - return kErrorExitCode; - default: - return 0; - } + return static_cast(last_error_.value_or(DartErrorCode::NoError)); } void DidProcessTask() { - if (engine_) { - last_error_ = engine_->GetUIIsolateLastError(); - if (engine_->UIIsolateHasLivePorts()) { - // The UI isolate still has live ports and is running. Nothing to do - // just yet. - return; - } + last_error_ = shell_.GetUIIsolateLastError(); + if (shell_.EngineHasLivePorts()) { + // The UI isolate still has live ports and is running. Nothing to do + // just yet. + return; } if (run_forever_) { @@ -76,10 +62,10 @@ class ScriptCompletionTaskObserver { } private: - fml::WeakPtr engine_; + Shell& shell_; fml::RefPtr main_task_runner_; bool run_forever_ = false; - tonic::DartErrorHandleType last_error_ = tonic::kUnknownErrorType; + std::optional last_error_; bool has_terminated = false; FML_DISALLOW_COPY_AND_ASSIGN(ScriptCompletionTaskObserver); @@ -175,47 +161,30 @@ int RunTester(const flutter::Settings& settings, bool run_forever) { bool engine_did_run = false; - fml::AutoResetWaitableEvent sync_run_latch; - fml::TaskRunner::RunNowOrPostTask( - shell->GetTaskRunners().GetUITaskRunner(), - fml::MakeCopyable([&sync_run_latch, &completion_observer, - engine = shell->GetEngine(), - config = std::move(run_configuration), - &engine_did_run]() mutable { - fml::MessageLoop::GetCurrent().AddTaskObserver( - reinterpret_cast(&completion_observer), - [&completion_observer]() { completion_observer.DidProcessTask(); }); - if (engine->Run(std::move(config)) != - flutter::Engine::RunStatus::Failure) { - engine_did_run = true; - - flutter::ViewportMetrics metrics; - metrics.device_pixel_ratio = 3.0; - metrics.physical_width = 2400; // 800 at 3x resolution - metrics.physical_height = 1800; // 600 at 3x resolution - engine->SetViewportMetrics(metrics); - - } else { - FML_DLOG(ERROR) << "Could not launch the engine with configuration."; - } - sync_run_latch.Signal(); - })); - sync_run_latch.Wait(); + fml::MessageLoop::GetCurrent().AddTaskObserver( + reinterpret_cast(&completion_observer), + [&completion_observer]() { completion_observer.DidProcessTask(); }); + + shell->RunEngine(std::move(run_configuration), + [&engine_did_run](Engine::RunStatus run_status) mutable { + if (run_status != flutter::Engine::RunStatus::Failure) { + engine_did_run = true; + } + }); + + flutter::ViewportMetrics metrics; + metrics.device_pixel_ratio = 3.0; + metrics.physical_width = 2400; // 800 at 3x resolution + metrics.physical_height = 1800; // 600 at 3x resolution + shell->GetPlatformView()->SetViewportMetrics(metrics); // Run the message loop and wait for the script to do its thing. fml::MessageLoop::GetCurrent().Run(); // Cleanup the completion observer synchronously as it is living on the // stack. - fml::AutoResetWaitableEvent latch; - fml::TaskRunner::RunNowOrPostTask( - shell->GetTaskRunners().GetUITaskRunner(), - [&latch, &completion_observer] { - fml::MessageLoop::GetCurrent().RemoveTaskObserver( - reinterpret_cast(&completion_observer)); - latch.Signal(); - }); - latch.Wait(); + fml::MessageLoop::GetCurrent().RemoveTaskObserver( + reinterpret_cast(&completion_observer)); if (!engine_did_run) { // If the engine itself didn't have a chance to run, there is no point in