// 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. #include "sky/shell/ui/engine.h" #include "base/bind.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/threading/worker_pool.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "mojo/data_pipe_utils/data_pipe_utils.h" #include "mojo/public/cpp/application/connect.h" #include "services/asset_bundle/zip_asset_bundle.h" #include "sky/engine/bindings/mojo_services.h" #include "sky/engine/core/script/dart_init.h" #include "sky/engine/core/script/directory_asset_bundle.h" #include "sky/engine/core/script/ui_dart_state.h" #include "sky/engine/public/web/Sky.h" #include "sky/shell/dart/dart_library_provider_files.h" #include "sky/shell/shell.h" #include "sky/shell/ui/animator.h" #include "sky/shell/ui/flutter_font_selector.h" #include "sky/shell/ui/platform_impl.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPictureRecorder.h" namespace sky { namespace shell { namespace { PlatformImpl* g_platform_impl = nullptr; } // namespace using mojo::asset_bundle::ZipAssetBundle; using mojo::asset_bundle::ZipAssetService; Engine::Config::Config() {} Engine::Config::~Config() {} Engine::Engine(const Config& config, rasterizer::RasterizerPtr rasterizer) : config_(config), animator_(new Animator(config, rasterizer.Pass(), this)), binding_(this), activity_running_(false), have_surface_(false), weak_factory_(this) {} Engine::~Engine() {} base::WeakPtr Engine::GetWeakPtr() { return weak_factory_.GetWeakPtr(); } void Engine::Init() { TRACE_EVENT0("flutter", "Engine::Init"); DCHECK(!g_platform_impl); g_platform_impl = new PlatformImpl(); blink::initialize(g_platform_impl); } void Engine::BeginFrame(ftl::TimePoint frame_time) { TRACE_EVENT0("flutter", "Engine::BeginFrame"); if (sky_view_) sky_view_->BeginFrame(frame_time); } void Engine::RunFromSource(const std::string& main, const std::string& packages, const std::string& assets_directory) { TRACE_EVENT0("flutter", "Engine::RunFromSource"); // Assets. base::FilePath assets_directory_path = base::FilePath(assets_directory); ConfigureDirectoryAssetBundle(assets_directory_path); // .packages. base::FilePath packages_path = base::FilePath(std::string(packages)); if (packages_path.empty()) { base::FilePath main_dir = base::FilePath(main).DirName(); packages_path = main_dir.Append(FILE_PATH_LITERAL(".packages")); if (!base::PathExists(packages_path)) { packages_path = main_dir.Append(base::FilePath::kParentDirectory) .Append(FILE_PATH_LITERAL(".packages")); if (!base::PathExists(packages_path)) packages_path = base::FilePath(); } } DartLibraryProviderFiles* provider = new DartLibraryProviderFiles(); dart_library_provider_.reset(provider); if (!packages_path.empty()) provider->LoadPackagesMap(packages_path); RunFromLibrary(main); } void Engine::ConnectToEngine(mojo::InterfaceRequest request) { binding_.Bind(request.Pass()); } void Engine::OnOutputSurfaceCreated(const base::Closure& gpu_continuation) { config_.gpu_task_runner->PostTask(FROM_HERE, gpu_continuation); have_surface_ = true; StartAnimatorIfPossible(); if (sky_view_) ScheduleFrame(); } void Engine::OnOutputSurfaceDestroyed(const base::Closure& gpu_continuation) { have_surface_ = false; StopAnimator(); config_.gpu_task_runner->PostTask(FROM_HERE, gpu_continuation); } void Engine::SetServices(ServicesDataPtr services) { services_ = services.Pass(); if (services_->incoming_services) { incoming_services_ = mojo::ServiceProviderPtr::Create(services_->incoming_services.Pass()); service_provider_impl_.set_fallback_service_provider( incoming_services_.get()); } if (services_->frame_scheduler) { animator_->Reset(); animator_->set_frame_scheduler(services_->frame_scheduler.Pass()); } else { #if defined(OS_ANDROID) || defined(OS_IOS) || defined(OS_MACOSX) vsync::VSyncProviderPtr vsync_provider; if (services_->shell) { // We bind and unbind our Shell here, since this is the only place we use // it in this class. auto shell = mojo::ShellPtr::Create(services_->shell.Pass()); mojo::ConnectToService(shell.get(), "mojo:vsync", mojo::GetProxy(&vsync_provider)); services_->shell = shell.Pass(); } else { mojo::ConnectToService(incoming_services_.get(), mojo::GetProxy(&vsync_provider)); } animator_->Reset(); animator_->set_vsync_provider(vsync_provider.Pass()); #endif } } void Engine::OnViewportMetricsChanged(ViewportMetricsPtr metrics) { viewport_metrics_ = metrics.Pass(); if (sky_view_) sky_view_->SetViewportMetrics(viewport_metrics_); } void Engine::OnLocaleChanged(const mojo::String& language_code, const mojo::String& country_code) { language_code_ = language_code; country_code_ = country_code; if (sky_view_) sky_view_->SetLocale(language_code_, country_code_); } void Engine::OnPointerPacket(pointer::PointerPacketPtr packet) { TRACE_EVENT0("flutter", "Engine::OnPointerPacket"); // Convert the pointers' x and y coordinates to logical pixels. for (auto it = packet->pointers.begin(); it != packet->pointers.end(); ++it) { (*it)->x /= viewport_metrics_->device_pixel_ratio; (*it)->y /= viewport_metrics_->device_pixel_ratio; } if (sky_view_) sky_view_->HandlePointerPacket(packet); } void Engine::RunFromLibrary(const std::string& name) { TRACE_EVENT0("flutter", "Engine::RunFromLibrary"); sky_view_ = blink::SkyView::Create(this); sky_view_->CreateView(name); sky_view_->RunFromLibrary(name, dart_library_provider_.get()); sky_view_->SetViewportMetrics(viewport_metrics_); sky_view_->SetLocale(language_code_, country_code_); if (!initial_route_.empty()) sky_view_->PushRoute(initial_route_); } void Engine::RunFromSnapshotStream( const std::string& script_uri, mojo::ScopedDataPipeConsumerHandle snapshot) { TRACE_EVENT0("flutter", "Engine::RunFromSnapshotStream"); sky_view_ = blink::SkyView::Create(this); sky_view_->CreateView(script_uri); sky_view_->RunFromSnapshot(snapshot.Pass()); sky_view_->SetViewportMetrics(viewport_metrics_); sky_view_->SetLocale(language_code_, country_code_); if (!initial_route_.empty()) sky_view_->PushRoute(initial_route_); } void Engine::ConfigureZipAssetBundle(const mojo::String& path) { zip_asset_bundle_ = new ZipAssetBundle(base::FilePath(std::string{path}), base::WorkerPool::GetTaskRunner(true)); ZipAssetService::Create(mojo::GetProxy(&root_bundle_), zip_asset_bundle_); } void Engine::ConfigureDirectoryAssetBundle(const base::FilePath& path) { blink::DirectoryAssetBundleService::Create( mojo::GetProxy(&root_bundle_), path); } void Engine::RunFromPrecompiledSnapshot(const mojo::String& bundle_path) { TRACE_EVENT0("flutter", "Engine::RunFromPrecompiledSnapshot"); ConfigureZipAssetBundle(bundle_path); sky_view_ = blink::SkyView::Create(this); sky_view_->CreateView("http://localhost"); sky_view_->RunFromPrecompiledSnapshot(); sky_view_->SetViewportMetrics(viewport_metrics_); sky_view_->SetLocale(language_code_, country_code_); if (!initial_route_.empty()) sky_view_->PushRoute(initial_route_); } void Engine::RunFromFile(const mojo::String& main, const mojo::String& packages, const mojo::String& bundle) { TRACE_EVENT0("flutter", "Engine::RunFromFile"); std::string main_str(main); if (bundle.size() != 0) { // The specification of an FLX bundle is optional. ConfigureZipAssetBundle(bundle); } base::FilePath packages_path = base::FilePath(std::string(packages)); if (packages_path.empty()) { base::FilePath main_dir = base::FilePath(main_str).DirName(); packages_path = main_dir.Append(FILE_PATH_LITERAL(".packages")); if (!base::PathExists(packages_path)) { packages_path = main_dir.Append(base::FilePath::kParentDirectory) .Append(FILE_PATH_LITERAL(".packages")); if (!base::PathExists(packages_path)) packages_path = base::FilePath(); } } DartLibraryProviderFiles* provider = new DartLibraryProviderFiles(); dart_library_provider_.reset(provider); if (!packages_path.empty()) provider->LoadPackagesMap(packages_path); RunFromLibrary(main_str); } void Engine::RunFromBundle(const mojo::String& script_uri, const mojo::String& path) { TRACE_EVENT0("flutter", "Engine::RunFromBundle"); ConfigureZipAssetBundle(path); root_bundle_->GetAsStream(blink::kSnapshotAssetKey, base::Bind(&Engine::RunFromSnapshotStream, weak_factory_.GetWeakPtr(), script_uri)); } void Engine::RunFromBundleAndSnapshot(const mojo::String& script_uri, const mojo::String& bundle_path, const mojo::String& snapshot_path) { TRACE_EVENT0("flutter", "Engine::RunFromBundleAndSnapshot"); ConfigureZipAssetBundle(bundle_path); std::string snapshot_path_str = snapshot_path; zip_asset_bundle_->AddOverlayFile(blink::kSnapshotAssetKey, base::FilePath(snapshot_path_str)); root_bundle_->GetAsStream(blink::kSnapshotAssetKey, base::Bind(&Engine::RunFromSnapshotStream, weak_factory_.GetWeakPtr(), script_uri)); } void Engine::PushRoute(const mojo::String& route) { if (sky_view_) sky_view_->PushRoute(route); else initial_route_ = route; } void Engine::PopRoute() { if (sky_view_) sky_view_->PopRoute(); } void Engine::OnAppLifecycleStateChanged(sky::AppLifecycleState state) { switch (state) { case sky::AppLifecycleState::PAUSED: activity_running_ = false; StopAnimator(); break; case sky::AppLifecycleState::RESUMED: activity_running_ = true; StartAnimatorIfPossible(); break; } if (sky_view_) sky_view_->OnAppLifecycleStateChanged(state); } void Engine::DidCreateMainIsolate(Dart_Isolate isolate) { mojo::ServiceProviderPtr services_from_embedder; service_provider_bindings_.AddBinding( &service_provider_impl_, mojo::GetProxy(&services_from_embedder)); blink::MojoServices::Create(isolate, services_.Pass(), services_from_embedder.Pass(), root_bundle_.Pass()); if (zip_asset_bundle_) { FlutterFontSelector::install(zip_asset_bundle_); } } void Engine::DidCreateSecondaryIsolate(Dart_Isolate isolate) { mojo::ServiceProviderPtr services_from_embedder; mojo::InterfaceRequest request = mojo::GetProxy(&services_from_embedder); base::Closure closure = base::Bind(&Engine::BindToServiceProvider, weak_factory_.GetWeakPtr(), base::Passed(&request)); blink::Platform::current()->GetUITaskRunner()->PostTask( [closure]() { closure.Run(); }); blink::MojoServices::Create(isolate, nullptr, services_from_embedder.Pass(), nullptr); } void Engine::BindToServiceProvider( mojo::InterfaceRequest request) { service_provider_bindings_.AddBinding(&service_provider_impl_, request.Pass()); } void Engine::StopAnimator() { animator_->Stop(); } void Engine::StartAnimatorIfPossible() { if (activity_running_ && have_surface_) animator_->Start(); } void Engine::ScheduleFrame() { animator_->RequestFrame(); } void Engine::FlushRealTimeEvents() { animator_->FlushRealTimeEvents(); } void Engine::Render(std::unique_ptr layer_tree) { if (!layer_tree) return; if (viewport_metrics_) { layer_tree->set_scene_version(viewport_metrics_->scene_version); layer_tree->set_frame_size(SkISize::Make( viewport_metrics_->physical_width, viewport_metrics_->physical_height)); } else { layer_tree->set_scene_version(0); layer_tree->set_frame_size(SkISize::Make(0, 0)); } animator_->Render(std::move(layer_tree)); } } // namespace shell } // namespace sky