diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 9ea0a9af4c6d981b902d2802def76cb272d314a5..0d7d7ce15d911417e267d0f57b0c41cb80605cea 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -137,4 +137,16 @@ std::string RuntimeController::GetIsolateName() { return dart_controller_->dart_state()->debug_name(); } +bool RuntimeController::HasLivePorts() { + if (!dart_controller_) { + return false; + } + UIDartState* dart_state = dart_controller_->dart_state(); + if (!dart_state) { + return false; + } + DartState::Scope scope(dart_state); + return Dart_HasLivePorts(); +} + } // namespace blink diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 58446f9cce68c375e315b0be348508a38b2f5d75..074336d713140e78e4f87ca69e00f3895e3e88a0 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -44,6 +44,8 @@ class RuntimeController : public WindowClient, public IsolateClient { std::string GetIsolateName(); + bool HasLivePorts(); + private: explicit RuntimeController(RuntimeDelegate* client); diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 95becf136f1b44d66b280bae4f9b4e30cc9b0761..ea5dab7315cc6525ba8647faced542be726ade1c 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -155,6 +155,12 @@ std::string Engine::GetUIIsolateName() { return runtime_->GetIsolateName(); } +bool Engine::UIIsolateHasLivePorts() { + if (!runtime_) + return false; + return runtime_->HasLivePorts(); +} + void Engine::OnOutputSurfaceCreated(const ftl::Closure& gpu_continuation) { blink::Threads::Gpu()->PostTask(gpu_continuation); have_surface_ = true; diff --git a/shell/common/engine.h b/shell/common/engine.h index 792b19669a0238bb934ae00423e6ec862ed78fa4..3fb35c8a0f02d839c0d014030f545553d3822034 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -59,6 +59,8 @@ class Engine : public blink::RuntimeDelegate { std::string GetUIIsolateName(); + bool UIIsolateHasLivePorts(); + void OnOutputSurfaceCreated(const ftl::Closure& gpu_continuation); void OnOutputSurfaceDestroyed(const ftl::Closure& gpu_continuation); void SetViewportMetrics(const blink::ViewportMetrics& metrics); diff --git a/shell/platform/linux/main_linux.cc b/shell/platform/linux/main_linux.cc index 11d16ad60aaed44e953d9293d958023a2cb0a674..fc0d1b7d6332d857bd36fcc93a7691cf22d5b775 100644 --- a/shell/platform/linux/main_linux.cc +++ b/shell/platform/linux/main_linux.cc @@ -14,22 +14,57 @@ #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/linux/message_pump_glfw.h" #include "flutter/shell/platform/linux/platform_view_glfw.h" +#include "flutter/shell/testing/test_runner.h" #include "flutter/shell/testing/testing.h" +#include "flutter/sky/engine/public/web/Sky.h" namespace { -int RunNonInteractive() { +// Checks whether the engine's main Dart isolate has no pending work. If so, +// then exit the given message loop. +class ScriptCompletionTaskObserver : public base::MessageLoop::TaskObserver { + public: + ScriptCompletionTaskObserver(base::MessageLoop& main_message_loop) + : main_message_loop_(main_message_loop), prev_live_(false) {} + + void WillProcessTask(const base::PendingTask& pending_task) override {} + + void DidProcessTask(const base::PendingTask& pending_task) override { + shell::TestRunner& test_runner = shell::TestRunner::Shared(); + bool live = test_runner.platform_view().engine().UIIsolateHasLivePorts(); + if (prev_live_ && !live) + main_message_loop_.PostTask(FROM_HERE, + main_message_loop_.QuitWhenIdleClosure()); + prev_live_ = live; + } + + private: + base::MessageLoop& main_message_loop_; + bool prev_live_; +}; + +void RunNonInteractive() { base::MessageLoop message_loop; shell::Shell::InitStandalone(); + // Note that this task observer must be added after the observer that drains + // the microtask queue. + ScriptCompletionTaskObserver task_observer(message_loop); + blink::Threads::UI()->PostTask([&task_observer] { + base::MessageLoop::current()->AddTaskObserver(&task_observer); + }); + if (!shell::InitForTesting()) { shell::PrintUsage("sky_shell"); - return 1; + exit(1); } message_loop.Run(); - return 0; + + // The script has completed and the engine may not be in a clean state, + // so just stop the process. + exit(0); } static bool IsDartFile(const std::string& path) { @@ -98,7 +133,8 @@ int main(int argc, const char* argv[]) { if (command_line.HasSwitch( shell::FlagForSwitch(shell::Switch::NonInteractive))) { - return RunNonInteractive(); + RunNonInteractive(); + return 0; } return RunInteractive(); diff --git a/shell/testing/test_runner.h b/shell/testing/test_runner.h index dc2feb8fd1d018290fff9c6ed81b4e20860d130b..3322f919afb2bb3c6064f39cc0191c59b11f1aff 100644 --- a/shell/testing/test_runner.h +++ b/shell/testing/test_runner.h @@ -26,6 +26,8 @@ class TestRunner { void Run(const TestDescriptor& test); + PlatformView& platform_view() { return *platform_view_; } + private: TestRunner(); ~TestRunner();