// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #define RAPIDJSON_HAS_STDSTRING 1 #include "flutter/shell/common/shell.h" #include #include #include #include "flutter/assets/directory_asset_bundle.h" #include "flutter/fml/file.h" #include "flutter/fml/icu_util.h" #include "flutter/fml/log_settings.h" #include "flutter/fml/logging.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/message_loop.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" #include "flutter/fml/unique_fd.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/start_up.h" #include "flutter/shell/common/engine.h" #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/vsync_waiter.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/skia/include/core/SkGraphics.h" #include "third_party/tonic/common/log.h" #ifdef ERROR #undef ERROR #endif namespace shell { std::unique_ptr Shell::CreateShellOnPlatformThread( blink::TaskRunners task_runners, blink::Settings settings, fml::RefPtr isolate_snapshot, fml::RefPtr shared_snapshot, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer) { if (!task_runners.IsValid()) { return nullptr; } auto shell = std::unique_ptr(new Shell(task_runners, settings)); // Create the platform view on the platform thread (this thread). auto platform_view = on_create_platform_view(*shell.get()); if (!platform_view || !platform_view->GetWeakPtr()) { return nullptr; } // Ask the platform view for the vsync waiter. This will be used by the engine // to create the animator. auto vsync_waiter = platform_view->CreateVSyncWaiter(); if (!vsync_waiter) { return nullptr; } // Create the IO manager on the IO thread. The IO manager must be initialized // first because it has state that the other subsystems depend on. It must // first be booted and the necessary references obtained to initialize the // other subsystems. fml::AutoResetWaitableEvent io_latch; std::unique_ptr io_manager; fml::WeakPtr resource_context; fml::RefPtr unref_queue; auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner(); fml::TaskRunner::RunNowOrPostTask( io_task_runner, [&io_latch, // &io_manager, // &resource_context, // &unref_queue, // &platform_view, // io_task_runner // ]() { io_manager = std::make_unique( platform_view->CreateResourceContext(), io_task_runner); resource_context = io_manager->GetResourceContext(); unref_queue = io_manager->GetSkiaUnrefQueue(); io_latch.Signal(); }); io_latch.Wait(); // Create the rasterizer on the GPU thread. fml::AutoResetWaitableEvent gpu_latch; std::unique_ptr rasterizer; fml::TaskRunner::RunNowOrPostTask( task_runners.GetGPUTaskRunner(), [&gpu_latch, // &rasterizer, // on_create_rasterizer, // shell = shell.get() // ]() { if (auto new_rasterizer = on_create_rasterizer(*shell)) { rasterizer = std::move(new_rasterizer); } gpu_latch.Signal(); }); // Create the engine on the UI thread. fml::AutoResetWaitableEvent ui_latch; std::unique_ptr engine; fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetUITaskRunner(), fml::MakeCopyable([&ui_latch, // &engine, // shell = shell.get(), // isolate_snapshot = std::move(isolate_snapshot), // shared_snapshot = std::move(shared_snapshot), // vsync_waiter = std::move(vsync_waiter), // resource_context = std::move(resource_context), // unref_queue = std::move(unref_queue) // ]() mutable { const auto& task_runners = shell->GetTaskRunners(); // The animator is owned by the UI thread but it gets its vsync pulses // from the platform. auto animator = std::make_unique(*shell, task_runners, std::move(vsync_waiter)); engine = std::make_unique(*shell, // shell->GetDartVM(), // std::move(isolate_snapshot), // std::move(shared_snapshot), // task_runners, // shell->GetSettings(), // std::move(animator), // std::move(resource_context), // std::move(unref_queue) // ); ui_latch.Signal(); })); gpu_latch.Wait(); ui_latch.Wait(); // We are already on the platform thread. So there is no platform latch to // wait on. if (!shell->Setup(std::move(platform_view), // std::move(engine), // std::move(rasterizer), // std::move(io_manager)) // ) { return nullptr; } return shell; } static void RecordStartupTimestamp() { if (blink::engine_main_enter_ts == 0) { blink::engine_main_enter_ts = Dart_TimelineGetMicros(); } } // Though there can be multiple shells, some settings apply to all components in // the process. These have to be setup before the shell or any of its // sub-components can be initialized. In a perfect world, this would be empty. // TODO(chinmaygarde): The unfortunate side effect of this call is that settings // that cause shell initialization failures will still lead to some of their // settings being applied. static void PerformInitializationTasks(const blink::Settings& settings) { static std::once_flag gShellSettingsInitialization = {}; std::call_once(gShellSettingsInitialization, [&settings] { RecordStartupTimestamp(); { fml::LogSettings log_settings; log_settings.min_log_level = settings.verbose_logging ? fml::LOG_INFO : fml::LOG_ERROR; fml::SetLogSettings(log_settings); } tonic::SetLogHandler( [](const char* message) { FML_LOG(ERROR) << message; }); if (settings.trace_skia) { InitSkiaEventTracer(settings.trace_skia); } if (!settings.skia_deterministic_rendering_on_cpu) { SkGraphics::Init(); } else { FML_DLOG(INFO) << "Skia deterministic rendering is enabled."; } if (settings.icu_data_path.size() != 0) { fml::icu::InitializeICU(settings.icu_data_path); } else { FML_DLOG(WARNING) << "Skipping ICU initialization in the shell."; } }); } std::unique_ptr Shell::Create( blink::TaskRunners task_runners, blink::Settings settings, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer) { PerformInitializationTasks(settings); auto vm = blink::DartVM::ForProcess(settings); FML_CHECK(vm) << "Must be able to initialize the VM."; return Shell::Create(std::move(task_runners), // std::move(settings), // vm->GetIsolateSnapshot(), // blink::DartSnapshot::Empty(), // std::move(on_create_platform_view), // std::move(on_create_rasterizer) // ); } std::unique_ptr Shell::Create( blink::TaskRunners task_runners, blink::Settings settings, fml::RefPtr isolate_snapshot, fml::RefPtr shared_snapshot, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer) { PerformInitializationTasks(settings); if (!task_runners.IsValid() || !on_create_platform_view || !on_create_rasterizer) { return nullptr; } fml::AutoResetWaitableEvent latch; std::unique_ptr shell; fml::TaskRunner::RunNowOrPostTask( task_runners.GetPlatformTaskRunner(), [&latch, // &shell, // task_runners = std::move(task_runners), // settings, // isolate_snapshot = std::move(isolate_snapshot), // shared_snapshot = std::move(shared_snapshot), // on_create_platform_view, // on_create_rasterizer // ]() { shell = CreateShellOnPlatformThread(std::move(task_runners), // settings, // std::move(isolate_snapshot), // std::move(shared_snapshot), // on_create_platform_view, // on_create_rasterizer // ); latch.Signal(); }); latch.Wait(); return shell; } Shell::Shell(blink::TaskRunners task_runners, blink::Settings settings) : task_runners_(std::move(task_runners)), settings_(std::move(settings)), vm_(blink::DartVM::ForProcess(settings_)) { FML_DCHECK(task_runners_.IsValid()); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // Install service protocol handlers. service_protocol_handlers_[blink::ServiceProtocol::kScreenshotExtensionName .ToString()] = { task_runners_.GetGPUTaskRunner(), std::bind(&Shell::OnServiceProtocolScreenshot, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_[blink::ServiceProtocol::kScreenshotSkpExtensionName .ToString()] = { task_runners_.GetGPUTaskRunner(), std::bind(&Shell::OnServiceProtocolScreenshotSKP, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_[blink::ServiceProtocol::kRunInViewExtensionName .ToString()] = { task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolRunInView, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_ [blink::ServiceProtocol::kFlushUIThreadTasksExtensionName.ToString()] = { task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolFlushUIThreadTasks, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_ [blink::ServiceProtocol::kSetAssetBundlePathExtensionName.ToString()] = { task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this, std::placeholders::_1, std::placeholders::_2)}; } Shell::~Shell() { if (auto vm = blink::DartVM::ForProcessIfInitialized()) { vm->GetServiceProtocol().RemoveHandler(this); } fml::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch; fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), fml::MakeCopyable([engine = std::move(engine_), &ui_latch]() mutable { engine.reset(); ui_latch.Signal(); })); ui_latch.Wait(); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetGPUTaskRunner(), fml::MakeCopyable( [rasterizer = std::move(rasterizer_), &gpu_latch]() mutable { rasterizer.reset(); gpu_latch.Signal(); })); gpu_latch.Wait(); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetIOTaskRunner(), fml::MakeCopyable([io_manager = std::move(io_manager_), platform_view = platform_view_.get(), &io_latch]() mutable { io_manager.reset(); if (platform_view) { platform_view->ReleaseResourceContext(); } io_latch.Signal(); })); io_latch.Wait(); // The platform view must go last because it may be holding onto platform side // counterparts to resources owned by subsystems running on other threads. For // example, the NSOpenGLContext on the Mac. fml::TaskRunner::RunNowOrPostTask( task_runners_.GetPlatformTaskRunner(), fml::MakeCopyable([platform_view = std::move(platform_view_), &platform_latch]() mutable { platform_view.reset(); platform_latch.Signal(); })); platform_latch.Wait(); } bool Shell::IsSetup() const { return is_setup_; } bool Shell::Setup(std::unique_ptr platform_view, std::unique_ptr engine, std::unique_ptr rasterizer, std::unique_ptr io_manager) { if (is_setup_) { return false; } if (!platform_view || !engine || !rasterizer || !io_manager) { return false; } platform_view_ = std::move(platform_view); engine_ = std::move(engine); rasterizer_ = std::move(rasterizer); io_manager_ = std::move(io_manager); is_setup_ = true; if (auto vm = blink::DartVM::ForProcessIfInitialized()) { vm->GetServiceProtocol().AddHandler(this); } return true; } const blink::Settings& Shell::GetSettings() const { return settings_; } const blink::TaskRunners& Shell::GetTaskRunners() const { return task_runners_; } fml::WeakPtr Shell::GetRasterizer() { FML_DCHECK(is_setup_); return rasterizer_->GetWeakPtr(); } fml::WeakPtr Shell::GetEngine() { FML_DCHECK(is_setup_); return engine_->GetWeakPtr(); } fml::WeakPtr Shell::GetPlatformView() { FML_DCHECK(is_setup_); return platform_view_->GetWeakPtr(); } blink::DartVM& Shell::GetDartVM() const { return *vm_; } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewCreated(std::unique_ptr surface) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // Note: // This is a synchronous operation because certain platforms depend on // setup/suspension of all activities that may be interacting with the GPU in // a synchronous fashion. fml::AutoResetWaitableEvent latch; auto gpu_task = fml::MakeCopyable([rasterizer = rasterizer_->GetWeakPtr(), // surface = std::move(surface), // &latch]() mutable { if (rasterizer) { rasterizer->Setup(std::move(surface)); } // Step 2: All done. Signal the latch that the platform thread is waiting // on. latch.Signal(); }); auto ui_task = [engine = engine_->GetWeakPtr(), // gpu_task_runner = task_runners_.GetGPUTaskRunner(), // gpu_task // ] { if (engine) { engine->OnOutputSurfaceCreated(); } // Step 1: Next, tell the GPU thread that it should create a surface for its // rasterizer. fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); }; // Step 0: Post a task onto the UI thread to tell the engine that it has an // output surface. fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); latch.Wait(); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewDestroyed() { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // Note: // This is a synchronous operation because certain platforms depend on // setup/suspension of all activities that may be interacting with the GPU in // a synchronous fashion. fml::AutoResetWaitableEvent latch; auto io_task = [io_manager = io_manager_.get(), &latch]() { // Execute any pending Skia object deletions while GPU access is still // allowed. io_manager->GetSkiaUnrefQueue()->Drain(); // Step 3: All done. Signal the latch that the platform thread is waiting // on. latch.Signal(); }; auto gpu_task = [rasterizer = rasterizer_->GetWeakPtr(), io_task_runner = task_runners_.GetIOTaskRunner(), io_task]() { if (rasterizer) { rasterizer->Teardown(); } // Step 2: Next, tell the IO thread to complete its remaining work. fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task); }; auto ui_task = [engine = engine_->GetWeakPtr(), gpu_task_runner = task_runners_.GetGPUTaskRunner(), gpu_task]() { if (engine) { engine->OnOutputSurfaceDestroyed(); } // Step 1: Next, tell the GPU thread that its rasterizer should suspend // access to the underlying surface. fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); }; // Step 0: Post a task onto the UI thread to tell the engine that its output // surface is about to go away. fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); latch.Wait(); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewSetViewportMetrics( const blink::ViewportMetrics& metrics) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask( [engine = engine_->GetWeakPtr(), metrics]() { if (engine) { engine->SetViewportMetrics(metrics); } }); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewDispatchPlatformMessage( fml::RefPtr message) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask( [engine = engine_->GetWeakPtr(), message = std::move(message)] { if (engine) { engine->DispatchPlatformMessage(std::move(message)); } }); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( [engine = engine_->GetWeakPtr(), packet = std::move(packet)] { if (engine) { engine->DispatchPointerDataPacket(*packet); } })); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewDispatchSemanticsAction(int32_t id, blink::SemanticsAction action, std::vector args) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask( [engine = engine_->GetWeakPtr(), id, action, args = std::move(args)] { if (engine) { engine->DispatchSemanticsAction(id, action, std::move(args)); } }); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewSetSemanticsEnabled(bool enabled) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask( [engine = engine_->GetWeakPtr(), enabled] { if (engine) { engine->SetSemanticsEnabled(enabled); } }); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewSetAccessibilityFeatures(int32_t flags) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask( [engine = engine_->GetWeakPtr(), flags] { if (engine) { engine->SetAccessibilityFeatures(flags); } }); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewRegisterTexture( std::shared_ptr texture) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture] { if (rasterizer) { if (auto registry = rasterizer->GetTextureRegistry()) { registry->RegisterTexture(texture); } } }); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewUnregisterTexture(int64_t texture_id) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { if (rasterizer) { if (auto registry = rasterizer->GetTextureRegistry()) { registry->UnregisterTexture(texture_id); } } }); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // Tell the rasterizer that one of its textures has a new frame available. task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { auto registry = rasterizer->GetTextureRegistry(); if (!registry) { return; } auto texture = registry->GetTexture(texture_id); if (!texture) { return; } texture->MarkNewFrameAvailable(); }); // Schedule a new frame without having to rebuild the layer tree. task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() { if (engine) { engine->ScheduleFrame(false); } }); } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewSetNextFrameCallback(fml::closure closure) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), closure = std::move(closure)]() { if (rasterizer) { rasterizer->SetNextFrameCallback(std::move(closure)); } }); } // |shell::Animator::Delegate| void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_time) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (engine_) { engine_->BeginFrame(frame_time); } } // |shell::Animator::Delegate| void Shell::OnAnimatorNotifyIdle(int64_t deadline) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (engine_) { engine_->NotifyIdle(deadline); } } // |shell::Animator::Delegate| void Shell::OnAnimatorDraw( fml::RefPtr> pipeline) { FML_DCHECK(is_setup_); task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), pipeline = std::move(pipeline)]() { if (rasterizer) { rasterizer->Draw(pipeline); } }); } // |shell::Animator::Delegate| void Shell::OnAnimatorDrawLastLayerTree() { FML_DCHECK(is_setup_); task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr()]() { if (rasterizer) { rasterizer->DrawLastLayerTree(); } }); } // |shell::Engine::Delegate| void Shell::OnEngineUpdateSemantics( blink::SemanticsNodeUpdates update, blink::CustomAccessibilityActionUpdates actions) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetPlatformTaskRunner()->PostTask( [view = platform_view_->GetWeakPtr(), update = std::move(update), actions = std::move(actions)] { if (view) { view->UpdateSemantics(std::move(update), std::move(actions)); } }); } // |shell::Engine::Delegate| void Shell::OnEngineHandlePlatformMessage( fml::RefPtr message) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetPlatformTaskRunner()->PostTask( [view = platform_view_->GetWeakPtr(), message = std::move(message)]() { if (view) { view->HandlePlatformMessage(std::move(message)); } }); } // |shell::Engine::Delegate| void Shell::OnPreEngineRestart() { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( task_runners_.GetPlatformTaskRunner(), [view = platform_view_->GetWeakPtr(), &latch]() { if (view) { view->OnPreEngineRestart(); } latch.Signal(); }); // This is blocking as any embedded platform views has to be flushed before // we re-run the Dart code. latch.Wait(); } // |blink::ServiceProtocol::Handler| fml::RefPtr Shell::GetServiceProtocolHandlerTaskRunner( fml::StringView method) const { FML_DCHECK(is_setup_); auto found = service_protocol_handlers_.find(method.ToString()); if (found != service_protocol_handlers_.end()) { return found->second.first; } return task_runners_.GetUITaskRunner(); } // |blink::ServiceProtocol::Handler| bool Shell::HandleServiceProtocolMessage( fml::StringView method, // one if the extension names specified above. const ServiceProtocolMap& params, rapidjson::Document& response) { auto found = service_protocol_handlers_.find(method.ToString()); if (found != service_protocol_handlers_.end()) { return found->second.second(params, response); } return false; } // |blink::ServiceProtocol::Handler| blink::ServiceProtocol::Handler::Description Shell::GetServiceProtocolDescription() const { return { engine_->GetUIIsolateMainPort(), engine_->GetUIIsolateName(), }; } static void ServiceProtocolParameterError(rapidjson::Document& response, std::string error_details) { auto& allocator = response.GetAllocator(); response.SetObject(); const int64_t kInvalidParams = -32602; response.AddMember("code", kInvalidParams, allocator); response.AddMember("message", "Invalid params", allocator); { rapidjson::Value details(rapidjson::kObjectType); details.AddMember("details", error_details, allocator); response.AddMember("data", details, allocator); } } static void ServiceProtocolFailureError(rapidjson::Document& response, std::string message) { auto& allocator = response.GetAllocator(); response.SetObject(); const int64_t kJsonServerError = -32000; response.AddMember("code", kJsonServerError, allocator); response.AddMember("message", message, allocator); } // Service protocol handler bool Shell::OnServiceProtocolScreenshot( const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); auto screenshot = rasterizer_->ScreenshotLastLayerTree( Rasterizer::ScreenshotType::CompressedImage, true); if (screenshot.data) { response.SetObject(); auto& allocator = response.GetAllocator(); response.AddMember("type", "Screenshot", allocator); rapidjson::Value image; image.SetString(static_cast(screenshot.data->data()), screenshot.data->size(), allocator); response.AddMember("screenshot", image, allocator); return true; } ServiceProtocolFailureError(response, "Could not capture image screenshot."); return false; } // Service protocol handler bool Shell::OnServiceProtocolScreenshotSKP( const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); auto screenshot = rasterizer_->ScreenshotLastLayerTree( Rasterizer::ScreenshotType::SkiaPicture, true); if (screenshot.data) { response.SetObject(); auto& allocator = response.GetAllocator(); response.AddMember("type", "ScreenshotSkp", allocator); rapidjson::Value skp; skp.SetString(static_cast(screenshot.data->data()), screenshot.data->size(), allocator); response.AddMember("skp", skp, allocator); return true; } ServiceProtocolFailureError(response, "Could not capture SKP screenshot."); return false; } // Service protocol handler bool Shell::OnServiceProtocolRunInView( const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (params.count("mainScript") == 0) { ServiceProtocolParameterError(response, "'mainScript' parameter is missing."); return false; } // TODO(chinmaygarde): In case of hot-reload from .dill files, the packages // file is ignored. Currently, the tool is passing a junk packages file to // pass this check. Update the service protocol interface and remove this // workaround. if (params.count("packagesFile") == 0) { ServiceProtocolParameterError(response, "'packagesFile' parameter is missing."); return false; } if (params.count("assetDirectory") == 0) { ServiceProtocolParameterError(response, "'assetDirectory' parameter is missing."); return false; } auto main_script_file = fml::paths::AbsolutePath(params.at("mainScript").ToString()); auto main_script_file_mapping = std::make_unique(main_script_file, false); auto isolate_configuration = blink::DartVM::IsKernelMapping(main_script_file_mapping.get()) ? IsolateConfiguration::CreateForSnapshot( std::move(main_script_file_mapping)) : IsolateConfiguration::CreateForSource( main_script_file, params.at("packagesFile").ToString()); RunConfiguration configuration(std::move(isolate_configuration)); configuration.AddAssetResolver(std::make_unique( fml::OpenFile(params.at("assetDirectory").ToString().c_str(), fml::OpenPermission::kRead, true))); auto& allocator = response.GetAllocator(); response.SetObject(); if (engine_->Restart(std::move(configuration))) { response.AddMember("type", "Success", allocator); auto new_description = GetServiceProtocolDescription(); rapidjson::Value view(rapidjson::kObjectType); new_description.Write(this, view, allocator); response.AddMember("view", view, allocator); return true; } else { FML_DLOG(ERROR) << "Could not run configuration in engine."; ServiceProtocolFailureError(response, "Could not run configuration in engine."); return false; } FML_DCHECK(false); return false; } // Service protocol handler bool Shell::OnServiceProtocolFlushUIThreadTasks( const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); // This API should not be invoked by production code. // It can potentially starve the service isolate if the main isolate pauses // at a breakpoint or is in an infinite loop. // // It should be invoked from the VM Service and and blocks it until UI thread // tasks are processed. response.SetObject(); response.AddMember("type", "Success", response.GetAllocator()); return true; } // Service protocol handler bool Shell::OnServiceProtocolSetAssetBundlePath( const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (params.count("assetDirectory") == 0) { ServiceProtocolParameterError(response, "'assetDirectory' parameter is missing."); return false; } auto& allocator = response.GetAllocator(); response.SetObject(); auto asset_manager = fml::MakeRefCounted(); asset_manager->PushFront(std::make_unique( fml::OpenFile(params.at("assetDirectory").ToString().c_str(), fml::OpenPermission::kRead, true))); if (engine_->UpdateAssetManager(std::move(asset_manager))) { response.AddMember("type", "Success", allocator); auto new_description = GetServiceProtocolDescription(); rapidjson::Value view(rapidjson::kObjectType); new_description.Write(this, view, allocator); response.AddMember("view", view, allocator); return true; } else { FML_DLOG(ERROR) << "Could not update asset directory."; ServiceProtocolFailureError(response, "Could not update asset directory."); return false; } FML_DCHECK(false); return false; } Rasterizer::Screenshot Shell::Screenshot( Rasterizer::ScreenshotType screenshot_type, bool base64_encode) { TRACE_EVENT0("flutter", "Shell::Screenshot"); fml::AutoResetWaitableEvent latch; Rasterizer::Screenshot screenshot; fml::TaskRunner::RunNowOrPostTask( task_runners_.GetGPUTaskRunner(), [&latch, // rasterizer = GetRasterizer(), // &screenshot, // screenshot_type, // base64_encode // ]() { if (rasterizer) { screenshot = rasterizer->ScreenshotLastLayerTree(screenshot_type, base64_encode); } latch.Signal(); }); latch.Wait(); return screenshot; } } // namespace shell