| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "flutter/runtime/runtime_controller.h" |
| |
| #include "flutter/fml/message_loop.h" |
| #include "flutter/fml/trace_event.h" |
| #include "flutter/lib/ui/compositing/scene.h" |
| #include "flutter/lib/ui/ui_dart_state.h" |
| #include "flutter/lib/ui/window/platform_configuration.h" |
| #include "flutter/lib/ui/window/viewport_metrics.h" |
| #include "flutter/lib/ui/window/window.h" |
| #include "flutter/runtime/isolate_configuration.h" |
| #include "flutter/runtime/runtime_delegate.h" |
| #include "third_party/tonic/dart_message_handler.h" |
| |
| namespace flutter { |
| |
| RuntimeController::RuntimeController(RuntimeDelegate& client, |
| TaskRunners p_task_runners) |
| : client_(client), vm_(nullptr), task_runners_(p_task_runners) {} |
| |
| RuntimeController::RuntimeController( |
| RuntimeDelegate& p_client, |
| DartVM* p_vm, |
| fml::RefPtr<const DartSnapshot> p_isolate_snapshot, |
| TaskRunners p_task_runners, |
| fml::WeakPtr<SnapshotDelegate> p_snapshot_delegate, |
| fml::WeakPtr<HintFreedDelegate> p_hint_freed_delegate, |
| fml::WeakPtr<IOManager> p_io_manager, |
| fml::RefPtr<SkiaUnrefQueue> p_unref_queue, |
| fml::WeakPtr<ImageDecoder> p_image_decoder, |
| std::string p_advisory_script_uri, |
| std::string p_advisory_script_entrypoint, |
| const std::function<void(int64_t)>& idle_notification_callback, |
| const PlatformData& p_platform_data, |
| const fml::closure& p_isolate_create_callback, |
| const fml::closure& p_isolate_shutdown_callback, |
| std::shared_ptr<const fml::Mapping> p_persistent_isolate_data, |
| std::shared_ptr<VolatilePathTracker> p_volatile_path_tracker) |
| : client_(p_client), |
| vm_(p_vm), |
| isolate_snapshot_(std::move(p_isolate_snapshot)), |
| task_runners_(p_task_runners), |
| snapshot_delegate_(p_snapshot_delegate), |
| hint_freed_delegate_(p_hint_freed_delegate), |
| io_manager_(p_io_manager), |
| unref_queue_(p_unref_queue), |
| image_decoder_(p_image_decoder), |
| advisory_script_uri_(p_advisory_script_uri), |
| advisory_script_entrypoint_(p_advisory_script_entrypoint), |
| idle_notification_callback_(idle_notification_callback), |
| platform_data_(std::move(p_platform_data)), |
| isolate_create_callback_(p_isolate_create_callback), |
| isolate_shutdown_callback_(p_isolate_shutdown_callback), |
| persistent_isolate_data_(std::move(p_persistent_isolate_data)), |
| volatile_path_tracker_(std::move(p_volatile_path_tracker)) {} |
| |
| std::unique_ptr<RuntimeController> RuntimeController::Spawn( |
| RuntimeDelegate& client, |
| std::string advisory_script_uri, |
| std::string advisory_script_entrypoint, |
| const std::function<void(int64_t)>& idle_notification_callback, |
| const fml::closure& isolate_create_callback, |
| const fml::closure& isolate_shutdown_callback, |
| std::shared_ptr<const fml::Mapping> persistent_isolate_data) const { |
| auto result = std::make_unique<RuntimeController>( |
| client, vm_, isolate_snapshot_, task_runners_, snapshot_delegate_, |
| hint_freed_delegate_, io_manager_, unref_queue_, image_decoder_, |
| advisory_script_uri, advisory_script_entrypoint, |
| idle_notification_callback, platform_data_, isolate_create_callback, |
| isolate_shutdown_callback, persistent_isolate_data, |
| volatile_path_tracker_); |
| result->spawning_isolate_ = root_isolate_; |
| return result; |
| } |
| |
| RuntimeController::~RuntimeController() { |
| FML_DCHECK(Dart_CurrentIsolate() == nullptr); |
| std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock(); |
| if (root_isolate) { |
| root_isolate->SetReturnCodeCallback(nullptr); |
| auto result = root_isolate->Shutdown(); |
| if (!result) { |
| FML_DLOG(ERROR) << "Could not shutdown the root isolate."; |
| } |
| root_isolate_ = {}; |
| } |
| } |
| |
| bool RuntimeController::IsRootIsolateRunning() { |
| std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock(); |
| if (root_isolate) { |
| return root_isolate->GetPhase() == DartIsolate::Phase::Running; |
| } |
| return false; |
| } |
| |
| std::unique_ptr<RuntimeController> RuntimeController::Clone() const { |
| return std::unique_ptr<RuntimeController>(new RuntimeController( |
| client_, // |
| vm_, // |
| isolate_snapshot_, // |
| task_runners_, // |
| snapshot_delegate_, // |
| hint_freed_delegate_, // |
| io_manager_, // |
| unref_queue_, // |
| image_decoder_, // |
| advisory_script_uri_, // |
| advisory_script_entrypoint_, // |
| idle_notification_callback_, // |
| platform_data_, // |
| isolate_create_callback_, // |
| isolate_shutdown_callback_, // |
| persistent_isolate_data_, // |
| volatile_path_tracker_ // |
| )); |
| } |
| |
| bool RuntimeController::FlushRuntimeStateToIsolate() { |
| return SetViewportMetrics(platform_data_.viewport_metrics) && |
| SetLocales(platform_data_.locale_data) && |
| SetSemanticsEnabled(platform_data_.semantics_enabled) && |
| SetAccessibilityFeatures( |
| platform_data_.accessibility_feature_flags_) && |
| SetUserSettingsData(platform_data_.user_settings_data) && |
| SetLifecycleState(platform_data_.lifecycle_state); |
| } |
| |
| bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { |
| platform_data_.viewport_metrics = metrics; |
| |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->get_window(0)->UpdateWindowMetrics(metrics); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::SetLocales( |
| const std::vector<std::string>& locale_data) { |
| platform_data_.locale_data = locale_data; |
| |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->UpdateLocales(locale_data); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::SetUserSettingsData(const std::string& data) { |
| platform_data_.user_settings_data = data; |
| |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->UpdateUserSettingsData( |
| platform_data_.user_settings_data); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::SetLifecycleState(const std::string& data) { |
| platform_data_.lifecycle_state = data; |
| |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->UpdateLifecycleState( |
| platform_data_.lifecycle_state); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::SetSemanticsEnabled(bool enabled) { |
| platform_data_.semantics_enabled = enabled; |
| |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->UpdateSemanticsEnabled( |
| platform_data_.semantics_enabled); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::SetAccessibilityFeatures(int32_t flags) { |
| platform_data_.accessibility_feature_flags_ = flags; |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->UpdateAccessibilityFeatures( |
| platform_data_.accessibility_feature_flags_); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::BeginFrame(fml::TimePoint frame_time) { |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->BeginFrame(frame_time); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::ReportTimings(std::vector<int64_t> timings) { |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->ReportTimings(std::move(timings)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::NotifyIdle(int64_t deadline, size_t freed_hint) { |
| std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock(); |
| if (!root_isolate) { |
| return false; |
| } |
| |
| tonic::DartState::Scope scope(root_isolate); |
| |
| // Dart will use the freed hint at the next idle notification. Make sure to |
| // Update it with our latest value before calling NotifyIdle. |
| Dart_HintFreed(freed_hint); |
| Dart_NotifyIdle(deadline); |
| |
| // Idle notifications being in isolate scope are part of the contract. |
| if (idle_notification_callback_) { |
| TRACE_EVENT0("flutter", "EmbedderIdleNotification"); |
| idle_notification_callback_(deadline); |
| } |
| return true; |
| } |
| |
| bool RuntimeController::DispatchPlatformMessage( |
| fml::RefPtr<PlatformMessage> message) { |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage", |
| "mode", "basic"); |
| platform_configuration->DispatchPlatformMessage(std::move(message)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::DispatchPointerDataPacket( |
| const PointerDataPacket& packet) { |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| TRACE_EVENT1("flutter", "RuntimeController::DispatchPointerDataPacket", |
| "mode", "basic"); |
| platform_configuration->get_window(0)->DispatchPointerDataPacket(packet); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool RuntimeController::DispatchKeyDataPacket(const KeyDataPacket& packet, |
| KeyDataResponse callback) { |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| TRACE_EVENT1("flutter", "RuntimeController::DispatchKeyDataPacket", "mode", |
| "basic"); |
| uint64_t response_id = |
| platform_configuration->RegisterKeyDataResponse(std::move(callback)); |
| platform_configuration->get_window(0)->DispatchKeyDataPacket(packet, |
| response_id); |
| return true; |
| } |
| return false; |
| } |
| |
| bool RuntimeController::DispatchSemanticsAction(int32_t id, |
| SemanticsAction action, |
| std::vector<uint8_t> args) { |
| TRACE_EVENT1("flutter", "RuntimeController::DispatchSemanticsAction", "mode", |
| "basic"); |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| platform_configuration->DispatchSemanticsAction(id, action, |
| std::move(args)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| PlatformConfiguration* |
| RuntimeController::GetPlatformConfigurationIfAvailable() { |
| std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock(); |
| return root_isolate ? root_isolate->platform_configuration() : nullptr; |
| } |
| |
| // |PlatformConfigurationClient| |
| std::string RuntimeController::DefaultRouteName() { |
| return client_.DefaultRouteName(); |
| } |
| |
| // |PlatformConfigurationClient| |
| void RuntimeController::ScheduleFrame() { |
| client_.ScheduleFrame(); |
| } |
| |
| // |PlatformConfigurationClient| |
| void RuntimeController::Render(Scene* scene) { |
| client_.Render(scene->takeLayerTree()); |
| } |
| |
| // |PlatformConfigurationClient| |
| void RuntimeController::UpdateSemantics(SemanticsUpdate* update) { |
| if (platform_data_.semantics_enabled) { |
| client_.UpdateSemantics(update->takeNodes(), update->takeActions()); |
| } |
| } |
| |
| // |PlatformConfigurationClient| |
| void RuntimeController::HandlePlatformMessage( |
| fml::RefPtr<PlatformMessage> message) { |
| client_.HandlePlatformMessage(std::move(message)); |
| } |
| |
| // |PlatformConfigurationClient| |
| FontCollection& RuntimeController::GetFontCollection() { |
| return client_.GetFontCollection(); |
| } |
| |
| // |PlatformConfigurationClient| |
| void RuntimeController::UpdateIsolateDescription(const std::string isolate_name, |
| int64_t isolate_port) { |
| client_.UpdateIsolateDescription(isolate_name, isolate_port); |
| } |
| |
| // |PlatformConfigurationClient| |
| void RuntimeController::SetNeedsReportTimings(bool value) { |
| client_.SetNeedsReportTimings(value); |
| } |
| |
| // |PlatformConfigurationClient| |
| std::shared_ptr<const fml::Mapping> |
| RuntimeController::GetPersistentIsolateData() { |
| return persistent_isolate_data_; |
| } |
| |
| // |PlatformConfigurationClient| |
| std::unique_ptr<std::vector<std::string>> |
| RuntimeController::ComputePlatformResolvedLocale( |
| const std::vector<std::string>& supported_locale_data) { |
| return client_.ComputePlatformResolvedLocale(supported_locale_data); |
| } |
| |
| Dart_Port RuntimeController::GetMainPort() { |
| std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock(); |
| return root_isolate ? root_isolate->main_port() : ILLEGAL_PORT; |
| } |
| |
| std::string RuntimeController::GetIsolateName() { |
| std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock(); |
| return root_isolate ? root_isolate->debug_name() : ""; |
| } |
| |
| bool RuntimeController::HasLivePorts() { |
| std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock(); |
| if (!root_isolate) { |
| return false; |
| } |
| tonic::DartState::Scope scope(root_isolate); |
| return Dart_HasLivePorts(); |
| } |
| |
| tonic::DartErrorHandleType RuntimeController::GetLastError() { |
| std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock(); |
| return root_isolate ? root_isolate->GetLastError() : tonic::kNoError; |
| } |
| |
| bool RuntimeController::LaunchRootIsolate( |
| const Settings& settings, |
| std::optional<std::string> dart_entrypoint, |
| std::optional<std::string> dart_entrypoint_library, |
| std::unique_ptr<IsolateConfiguration> isolate_configuration) { |
| if (root_isolate_.lock()) { |
| FML_LOG(ERROR) << "Root isolate was already running."; |
| return false; |
| } |
| |
| auto strong_root_isolate = |
| DartIsolate::CreateRunningRootIsolate( |
| settings, // |
| isolate_snapshot_, // |
| task_runners_, // |
| std::make_unique<PlatformConfiguration>(this), // |
| snapshot_delegate_, // |
| hint_freed_delegate_, // |
| io_manager_, // |
| unref_queue_, // |
| image_decoder_, // |
| advisory_script_uri_, // |
| advisory_script_entrypoint_, // |
| DartIsolate::Flags{}, // |
| isolate_create_callback_, // |
| isolate_shutdown_callback_, // |
| dart_entrypoint, // |
| dart_entrypoint_library, // |
| std::move(isolate_configuration), // |
| volatile_path_tracker_, // |
| spawning_isolate_.lock().get() // |
| ) |
| .lock(); |
| |
| if (!strong_root_isolate) { |
| FML_LOG(ERROR) << "Could not create root isolate."; |
| return false; |
| } |
| |
| // The root isolate ivar is weak. |
| root_isolate_ = strong_root_isolate; |
| |
| // Capture by `this` here is safe because the callback is made by the dart |
| // state itself. The isolate (and its Dart state) is owned by this object and |
| // it will be collected before this object. |
| strong_root_isolate->SetReturnCodeCallback( |
| [this](uint32_t code) { root_isolate_return_code_ = code; }); |
| |
| if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { |
| tonic::DartState::Scope scope(strong_root_isolate); |
| platform_configuration->DidCreateIsolate(); |
| if (!FlushRuntimeStateToIsolate()) { |
| FML_DLOG(ERROR) << "Could not set up initial isolate state."; |
| } |
| } else { |
| FML_DCHECK(false) << "RuntimeController created without window binding."; |
| } |
| |
| FML_DCHECK(Dart_CurrentIsolate() == nullptr); |
| |
| client_.OnRootIsolateCreated(); |
| |
| return true; |
| } |
| |
| std::optional<std::string> RuntimeController::GetRootIsolateServiceID() const { |
| if (auto isolate = root_isolate_.lock()) { |
| return isolate->GetServiceId(); |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<uint32_t> RuntimeController::GetRootIsolateReturnCode() { |
| return root_isolate_return_code_; |
| } |
| |
| uint64_t RuntimeController::GetRootIsolateGroup() const { |
| auto isolate = root_isolate_.lock(); |
| if (isolate) { |
| auto isolate_scope = tonic::DartIsolateScope(isolate->isolate()); |
| Dart_IsolateGroup isolate_group = Dart_CurrentIsolateGroup(); |
| return reinterpret_cast<uint64_t>(isolate_group); |
| } else { |
| return 0; |
| } |
| } |
| |
| void RuntimeController::LoadDartDeferredLibrary( |
| intptr_t loading_unit_id, |
| std::unique_ptr<const fml::Mapping> snapshot_data, |
| std::unique_ptr<const fml::Mapping> snapshot_instructions) { |
| root_isolate_.lock()->LoadLoadingUnit(loading_unit_id, |
| std::move(snapshot_data), |
| std::move(snapshot_instructions)); |
| } |
| |
| void RuntimeController::LoadDartDeferredLibraryError( |
| intptr_t loading_unit_id, |
| const std::string error_message, |
| bool transient) { |
| root_isolate_.lock()->LoadLoadingUnitError(loading_unit_id, error_message, |
| transient); |
| } |
| |
| void RuntimeController::RequestDartDeferredLibrary(intptr_t loading_unit_id) { |
| return client_.RequestDartDeferredLibrary(loading_unit_id); |
| } |
| |
| RuntimeController::Locale::Locale(std::string language_code_, |
| std::string country_code_, |
| std::string script_code_, |
| std::string variant_code_) |
| : language_code(language_code_), |
| country_code(country_code_), |
| script_code(script_code_), |
| variant_code(variant_code_) {} |
| |
| RuntimeController::Locale::~Locale() = default; |
| |
| } // namespace flutter |