blob: 45e496b75f88de645e333f96b94a358840f7567e [file] [log] [blame]
// 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 "flutter/sky/shell/ui/engine.h"
#include <unistd.h>
#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/assets/zip_asset_bundle.h"
#include "flutter/tonic/dart_library_provider_files.h"
#include "flutter/glue/movable_wrapper.h"
#include "flutter/glue/trace_event.h"
#include "lib/ftl/files/path.h"
#include "mojo/public/cpp/application/connect.h"
#include "flutter/sky/engine/bindings/mojo_services.h"
#include "flutter/sky/engine/core/script/dart_init.h"
#include "flutter/sky/engine/core/script/ui_dart_state.h"
#include "flutter/sky/engine/public/platform/Platform.h"
#include "flutter/sky/engine/public/web/Sky.h"
#include "flutter/sky/shell/shell.h"
#include "flutter/sky/shell/ui/animator.h"
#include "flutter/sky/shell/ui/flutter_font_selector.h"
#include "flutter/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 {
bool PathExists(const std::string& path) {
return access(path.c_str(), R_OK) == 0;
}
std::string FindPackagesPath(const std::string& main_dart) {
std::string directory = files::GetDirectoryName(main_dart);
std::string packages_path = directory + "/.packages";
if (!PathExists(packages_path)) {
directory = files::GetDirectoryName(directory);
packages_path = directory + "/.packages";
if (!PathExists(packages_path))
packages_path = std::string();
}
return packages_path;
}
PlatformImpl* g_platform_impl = nullptr;
} // namespace
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() {}
ftl::WeakPtr<Engine> 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.
ConfigureDirectoryAssetBundle(assets_directory);
// .packages.
std::string packages_path = packages;
if (packages_path.empty())
packages_path = FindPackagesPath(main);
DartLibraryProviderFiles* provider = new DartLibraryProviderFiles();
dart_library_provider_.reset(provider);
if (!packages_path.empty())
provider->LoadPackagesMap(packages_path);
RunFromLibrary(main);
}
Dart_Port Engine::GetUIIsolateMainPort() {
if (!sky_view_) {
return ILLEGAL_PORT;
}
return sky_view_->GetMainPort();
}
void Engine::ConnectToEngine(mojo::InterfaceRequest<SkyEngine> request) {
binding_.Bind(request.Pass());
}
void Engine::OnOutputSurfaceCreated(const ftl::Closure& gpu_continuation) {
config_.gpu_task_runner->PostTask(gpu_continuation);
have_surface_ = true;
StartAnimatorIfPossible();
if (sky_view_)
ScheduleFrame();
}
void Engine::OnOutputSurfaceDestroyed(const ftl::Closure& gpu_continuation) {
have_surface_ = false;
StopAnimator();
config_.gpu_task_runner->PostTask(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) {
asset_store_ = ftl::MakeRefCounted<blink::ZipAssetStore>(
path.get(), ftl::RefPtr<ftl::TaskRunner>(
blink::Platform::current()->GetIOTaskRunner()));
new blink::ZipAssetBundle(mojo::GetProxy(&root_bundle_), asset_store_);
}
void Engine::ConfigureDirectoryAssetBundle(const std::string& path) {
new blink::DirectoryAssetBundle(
mojo::GetProxy(&root_bundle_), path,
ftl::RefPtr<ftl::TaskRunner>(
blink::Platform::current()->GetIOTaskRunner()));
}
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_dart(main);
if (bundle.size() != 0) {
// The specification of an FLX bundle is optional.
ConfigureZipAssetBundle(bundle);
}
std::string packages_path = packages;
if (packages_path.empty())
packages_path = FindPackagesPath(main_dart);
DartLibraryProviderFiles* provider = new DartLibraryProviderFiles();
dart_library_provider_.reset(provider);
if (!packages_path.empty())
provider->LoadPackagesMap(packages_path);
RunFromLibrary(main_dart);
}
void Engine::RunFromBundle(const mojo::String& script_uri,
const mojo::String& path) {
TRACE_EVENT0("flutter", "Engine::RunFromBundle");
ConfigureZipAssetBundle(path);
mojo::DataPipe pipe;
asset_store_->GetAsStream(blink::kSnapshotAssetKey,
std::move(pipe.producer_handle));
RunFromSnapshotStream(script_uri, std::move(pipe.consumer_handle));
}
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);
asset_store_->AddOverlayFile(blink::kSnapshotAssetKey, snapshot_path);
mojo::DataPipe pipe;
asset_store_->GetAsStream(blink::kSnapshotAssetKey,
std::move(pipe.producer_handle));
RunFromSnapshotStream(script_uri, std::move(pipe.consumer_handle));
}
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 (asset_store_)
FlutterFontSelector::Install(asset_store_);
}
void Engine::DidCreateSecondaryIsolate(Dart_Isolate isolate) {
mojo::ServiceProviderPtr services_from_embedder;
auto request = glue::WrapMovable(mojo::GetProxy(&services_from_embedder));
ftl::WeakPtr<Engine> engine = weak_factory_.GetWeakPtr();
blink::Platform::current()->GetUITaskRunner()->PostTask(
[engine, request]() mutable {
if (engine)
engine->BindToServiceProvider(request.Unwrap());
});
blink::MojoServices::Create(isolate, nullptr,
std::move(services_from_embedder), nullptr);
}
void Engine::BindToServiceProvider(
mojo::InterfaceRequest<mojo::ServiceProvider> 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<flow::LayerTree> 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