未验证 提交 6baff4c8 编写于 作者: C Chinmay Garde 提交者: GitHub

Support multiple shells in a single process. (#4932)

* Support multiple shells in a single process.

The Flutter Engine currently works by initializing a singleton shell
instance. This shell has to be created on the platform thread. The shell
is responsible for creating the 3 main threads used by Flutter (UI, IO,
GPU) as well as initializing the Dart VM. The shell, references to task
runners of the main threads as well as all snapshots used for VM
initialization are stored in singleton objects. The Flutter shell only
creates the threads, rasterizers, contexts, etc. to fully support a
single Flutter application. Current support for multiple Flutter
applications is achieved by making multiple applications share the same
resources (via the platform views mechanism).

This scheme has the following limitations:

* The shell is a singleton and there is no way to tear it down. Once you
  run a Flutter application in a process, all resources managed by it
  will remain referenced till process termination.
* The threads on which the shell performs its operations are all
  singletons. These threads are never torn down and multiple Flutter
  applications (if present) have to compete with one another on these
  threads.
* Resources referenced by the Dart VM are leaked because the VM isn't
  shutdown even when there are no more Flutter views.
* The shell as a target does not compile on Fuchsia. The Fuchsia content
  handler uses specific dependencies of the shell to rebuild all the
  shell dependencies on its own. This leads to differences in frame
  scheduling, VM setup, service protocol endpoint setup, tracing, etc..
  Fuchsia is very much a second class citizen in this world.
* Since threads and message loops are managed by the engine, the engine
  has to know about threading and platform message loop interop on each
  supported platform.

Specific updates in this patch:

* The shell is no longer a singleton and the embedder holds the unique
  reference to the shell.
* Shell setup and teardown is deterministic.
* Threads are no longer managed by the shell. Instead, the shell is
  given a task runner configuration by the embedder.
* Since the shell does not own its threads, the embedder can control
  threads and the message loops operating on these threads. The shell is
  only given references to the task runners that execute tasks on these
  threads.
* The shell only needs task runner references. These references can be
  to the same task runner. So, if the embedder thinks that a particular
  Flutter application would not need all the threads, it can pass
  references to the same task runner. This effectively makes Flutter
  application run in single threaded mode. There are some places in the
  shell that make synchronous calls, these sites have been updated to
  ensure that they don’t deadlock.
* The test runner and the headless Dart code runner are now Flutter
  applications that are effectively single threaded (since they don’t
  have rendering concerns of big-boy Flutter application).
* The embedder has to guarantee that the threads and outlive the shell.
  It is easy for the embedder to make that guarantee because shell
  termination is deterministic.
* The embedder can create as many shell as it wants. Typically it
  creates a shell per Flutter application with its own task runner
  configuration. Most embedders obtain these task runners from threads
  dedicated to the shell. But, it is entirely possible that the embedder
  can obtain these task runners from a thread pool.
* There can only be one Dart VM in the process. The numerous shell
  interact with one another to manage the VM lifecycle. Once the last
  shell goes away, the VM does as well and hence all resources
  associated with the VM are collected.
* The shell as a target can now compile and run on Fuchsia. The current
  content handler has been removed from the Flutter engine source tree
  and a new implementation has been written that uses the new shell
  target.
* Isolate management has been significantly overhauled. There are no
  owning references to Dart isolates within the shell. The VM owns the
  only strong reference to the Dart isolate. The isolate that has window
  bindings is now called the root isolate. Child isolates can now be
  created from the root isolate and their bindings and thread
  configurations are now inherited from the root isolate.
* Terminating the shell terminates its root isolates as well as all the
  isolates spawned by this isolate. This is necessary be shell shutdown
  is deterministic and the embedder is free to collect the threads on
  which the isolates execute their tasks (and listen for mircrotasks
  flushes on).
* Launching the root isolate is now significantly overhauled. The shell
  side (non-owning) reference to an isolate is now a little state
  machine and illegal state transitions should be impossible (barring
  construction issues). This is the only way to manage Dart isolates in
  the shell (the shell does not use the C API is dart_api.h anymore).
* Once an isolate is launched, it must be prepared (and hence move to
  the ready phase) by associating a snapshot with the same. This
  snapshot can either be a precompiled snapshot, kernel snapshot, script
  snapshot or source file. Depending on the kind of data specified as a
  snapshot as well as the capabilities of the VM running in the process,
  isolate preparation can fail preparation with the right message.
* Asset management has been significantly overhauled. All asset
  resolution goes through an abstract asset resolver interface. An asset
  manager implements this interface and manages one or more child asset
  resolvers. These asset resolvers typically resolve assets from
  directories, ZIP files (legacy FLX assets if provided), APK bundles,
  FDIO namespaces, etc…
* Each launch of the shell requires a separate and fully configured
  asset resolver. This is necessary because launching isolates for the
  engine may require resolving snapshots as assets from the asset
  resolver. Asset resolvers can be shared by multiple launch instances
  in multiple shells and need to be thread safe.
* References to the command line object have been removed from the
  shell. Instead, the shell only takes a settings object that may be
  configured from the command line. This makes it easy for embedders and
  platforms that don’t have a command line (Fuchsia) to configure the
  shell. Consequently, there is only one spot where the various switches
  are read from the command line (by the embedder and not the shell) to
  form the settings object.
* All platform now respect the log tag (this was done only by Android
  till now) and each shell instance have its own log tag. This makes
  logs from multiple Flutter application in the same process (mainly
  Fuchsia) more easily decipherable.
* The per shell IO task runner now has a new component that is
  unfortunately named the IOManager. This component manages the IO
  GrContext (used for asynchronous texture uploads) that cooperates with
  the GrContext on the GPU task runner associated with the shell. The
  IOManager is also responsible for flushing tasks that collect Skia
  objects that reference GPU resources during deterministic shell
  shutdown.
* The embedder now has to be careful to only enable Blink on a single
  instance of the shell. Launching the legacy text layout and rendering
  engine multiple times is will trip assertions. The entirety of this
  runtime has been separated out into a separate object and can be
  removed in one go when the migration to libtxt is complete.
* There is a new test target for the various C++ objects that the shell
  uses to interact with the Dart VM (the shell no longer use the C API
  in dart_api.h). This allows engine developers to test VM/Isolate
  initialization and teardown without having the setup a full shell
  instance.
* There is a new test target for the testing a single shell instances
  without having to configure and launch an entire VM and associated
  root isolate.
* Mac, Linux & Windows used to have different target that created the
  flutter_tester referenced by the tool. This has now been converted
  into a single target that compiles on all platforms.
* WeakPointers vended by the fml::WeakPtrFactory(notice the difference
  between the same class in the fxl namespace) add threading checks on
  each use. This is enabled by getting rid of the “re-origination”
  feature of the WeakPtrFactory in the fxl namespace. The side effect of
  this is that all non-thread safe components have to be created, used
  and destroyed on the same thread. Numerous thread safety issues were
  caught by this extra assertion and have now been fixed.
  * Glossary of components that are only safe on a specific thread (and
    have the fml variants of the WeakPtrFactory):
    * Platform Thread: Shell
    * UI Thread: Engine, RuntimeDelegate, DartIsolate, Animator
    * GPU Thread: Rasterizer, Surface
    * IO Thread: IOManager

This patch was reviewed in smaller chunks in the following pull
requests. All comments from the pulls requests has been incorporated
into this patch:

* flutter/assets: https://github.com/flutter/engine/pull/4829
* flutter/common: https://github.com/flutter/engine/pull/4830
* flutter/content_handler: https://github.com/flutter/engine/pull/4831
* flutter/flow: https://github.com/flutter/engine/pull/4832
* flutter/fml: https://github.com/flutter/engine/pull/4833
* flutter/lib/snapshot: https://github.com/flutter/engine/pull/4834
* flutter/lib/ui: https://github.com/flutter/engine/pull/4835
* flutter/runtime: https://github.com/flutter/engine/pull/4836
* flutter/shell: https://github.com/flutter/engine/pull/4837
* flutter/synchronization: https://github.com/flutter/engine/pull/4838
* flutter/testing: https://github.com/flutter/engine/pull/4839
上级 31c5bb42
......@@ -10,14 +10,12 @@ group("flutter") {
public_deps = [
"$flutter_root/lib/snapshot:generate_snapshot_bin",
"$flutter_root/lib/snapshot:kernel_platform_files",
"$flutter_root/shell/testing",
"$flutter_root/sky",
"$flutter_root/third_party/txt",
]
if (flutter_runtime_mode != "debug") {
public_deps += [
"$flutter_root/lib/snapshot:entry_points_json_files",
]
public_deps += [ "$flutter_root/lib/snapshot:entry_points_json_files" ]
}
if (!is_fuchsia && !is_fuchsia_host) {
......@@ -45,20 +43,24 @@ group("flutter") {
"$flutter_root/shell/platform/embedder:flutter_embedder_framework",
]
}
if (!is_win) {
public_deps += [
"$flutter_root/shell/platform/embedder:embedder_unittests",
"$flutter_root/shell/platform/embedder:flutter_engine",
]
}
public_deps += [
"$flutter_root/flow:flow_unittests",
"$flutter_root/fml:fml_unittests",
"$flutter_root/runtime:runtime_unittests",
"$flutter_root/shell/common:shell_unittests",
"$flutter_root/sky/engine/wtf:wtf_unittests",
"$flutter_root/synchronization:synchronization_unittests",
"$flutter_root/third_party/txt:txt_unittests",
"//garnet/public/lib/fxl:fxl_unittests",
]
if (!is_win) {
public_deps += [
"$flutter_root/shell/platform/embedder:embedder_unittests",
"$flutter_root/shell/platform/embedder:flutter_engine",
]
}
}
}
......@@ -74,29 +76,23 @@ if (is_fuchsia) {
"$flutter_root/content_handler:aot",
]
if (flutter_runtime_mode != "release") {
deps += [
"//third_party/dart/runtime/observatory:embedded_archive_observatory",
]
deps += [ "//third_party/dart/runtime/observatory:embedded_archive_observatory" ]
}
binary = "flutter_aot_runner"
if (flutter_runtime_mode != "release") {
resources = [
{
path = rebase_path(
"$root_gen_dir/observatory/embedded_archive_observatory.tar")
dest = "observatory.tar"
},
]
resources = [ {
path = rebase_path(
"$root_gen_dir/observatory/embedded_archive_observatory.tar")
dest = "observatory.tar"
} ]
}
meta = [
{
path = rebase_path("content_handler/meta/sandbox")
dest = "sandbox"
},
]
meta = [ {
path = rebase_path("content_handler/meta/sandbox")
dest = "sandbox"
} ]
}
package("flutter_jit_runner") {
......@@ -104,29 +100,23 @@ if (is_fuchsia) {
"$flutter_root/content_handler:jit",
]
if (flutter_runtime_mode != "release") {
deps += [
"//third_party/dart/runtime/observatory:embedded_archive_observatory",
]
deps += [ "//third_party/dart/runtime/observatory:embedded_archive_observatory" ]
}
binary = "flutter_jit_runner"
if (flutter_runtime_mode != "release") {
resources = [
{
path = rebase_path(
"$root_gen_dir/observatory/embedded_archive_observatory.tar")
dest = "observatory.tar"
},
]
resources = [ {
path = rebase_path(
"$root_gen_dir/observatory/embedded_archive_observatory.tar")
dest = "observatory.tar"
} ]
}
meta = [
{
path = rebase_path("content_handler/meta/sandbox")
dest = "sandbox"
},
]
meta = [ {
path = rebase_path("content_handler/meta/sandbox")
dest = "sandbox"
} ]
}
} else {
group("dist") {
......
......@@ -115,7 +115,7 @@ allowed_hosts = [
]
deps = {
'src': 'https://github.com/flutter/buildroot.git' + '@' + '8dddd90bf943a8174913564353b30a3b11ee0f7a',
'src': 'https://github.com/flutter/buildroot.git' + '@' + '3cf97e01cdbd4bb920fa3d40282a56c4b2d62a58',
# Fuchsia compatibility
#
......
......@@ -4,16 +4,18 @@
source_set("assets") {
sources = [
"asset_provider.h",
"asset_manager.cc",
"asset_manager.h",
"asset_resolver.h",
"directory_asset_bundle.cc",
"directory_asset_bundle.h",
"unzipper_provider.cc",
"unzipper_provider.h",
"zip_asset_store.cc",
"zip_asset_store.h",
]
deps = [
"$flutter_root/common",
"$flutter_root/fml",
"$flutter_root/glue",
"//garnet/public/lib/fxl",
"//garnet/public/lib/zip",
......@@ -23,7 +25,5 @@ source_set("assets") {
"//third_party/zlib:minizip",
]
public_configs = [
"$flutter_root:config",
]
public_configs = [ "$flutter_root:config" ]
}
// Copyright 2017 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/assets/asset_manager.h"
#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/assets/zip_asset_store.h"
#include "flutter/glue/trace_event.h"
#include "lib/fxl/files/path.h"
namespace blink {
AssetManager::AssetManager() = default;
AssetManager::~AssetManager() = default;
void AssetManager::PushFront(std::unique_ptr<AssetResolver> resolver) {
if (resolver == nullptr || !resolver->IsValid()) {
return;
}
resolvers_.push_front(std::move(resolver));
}
void AssetManager::PushBack(std::unique_ptr<AssetResolver> resolver) {
if (resolver == nullptr || !resolver->IsValid()) {
return;
}
resolvers_.push_back(std::move(resolver));
}
// |blink::AssetResolver|
bool AssetManager::GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) const {
if (asset_name.size() == 0) {
return false;
}
TRACE_EVENT0("flutter", "AssetManager::GetAsBuffer");
for (const auto& resolver : resolvers_) {
if (resolver->GetAsBuffer(asset_name, data)) {
return true;
}
}
FXL_DLOG(ERROR) << "Could not find asset: " << asset_name;
return false;
}
// |blink::AssetResolver|
bool AssetManager::IsValid() const {
return resolvers_.size() > 0;
}
} // namespace blink
// Copyright 2017 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.
#ifndef FLUTTER_ASSETS_ASSET_MANAGER_H_
#define FLUTTER_ASSETS_ASSET_MANAGER_H_
#include <deque>
#include <memory>
#include <string>
#include "flutter/assets/asset_resolver.h"
#include "lib/fxl/files/unique_fd.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/ref_counted.h"
namespace blink {
class AssetManager final : public AssetResolver,
public fxl::RefCountedThreadSafe<AssetManager> {
public:
void PushFront(std::unique_ptr<AssetResolver> resolver);
void PushBack(std::unique_ptr<AssetResolver> resolver);
// |blink::AssetResolver|
bool IsValid() const override;
// |blink::AssetResolver|
bool GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) const override;
private:
std::deque<std::unique_ptr<AssetResolver>> resolvers_;
AssetManager();
~AssetManager();
FXL_DISALLOW_COPY_AND_ASSIGN(AssetManager);
FRIEND_MAKE_REF_COUNTED(AssetManager);
FRIEND_REF_COUNTED_THREAD_SAFE(AssetManager);
};
} // namespace blink
#endif // FLUTTER_ASSETS_ASSET_MANAGER_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Copyright 2017 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.
#ifndef FLUTTER_ASSETS_ASSET_PROVIDER_H_
#define FLUTTER_ASSETS_ASSET_PROVIDER_H_
#ifndef FLUTTER_ASSETS_ASSET_RESOLVER_H_
#define FLUTTER_ASSETS_ASSET_RESOLVER_H_
#include <string>
#include <vector>
#include "lib/fxl/memory/ref_counted.h"
#include "lib/fxl/macros.h"
namespace blink {
class AssetProvider
: public fxl::RefCountedThreadSafe<AssetProvider>
{
class AssetResolver {
public:
AssetResolver() = default;
virtual ~AssetResolver() = default;
virtual bool IsValid() const = 0;
virtual bool GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) = 0;
virtual ~AssetProvider() = default;
std::vector<uint8_t>* data) const = 0;
private:
FXL_DISALLOW_COPY_AND_ASSIGN(AssetResolver);
};
} // namespace blink
#endif // FLUTTER_ASSETS_ASSET_PROVIDER_H
#endif // FLUTTER_ASSETS_ASSET_RESOLVER_H_
......@@ -3,73 +3,54 @@
// found in the LICENSE file.
#include "flutter/assets/directory_asset_bundle.h"
#include "lib/fxl/build_config.h"
#include <fcntl.h>
#include <utility>
#include "flutter/fml/file.h"
#include "flutter/fml/mapping.h"
#include "lib/fxl/files/eintr_wrapper.h"
#include "lib/fxl/files/file.h"
#include "lib/fxl/files/path.h"
#include "lib/fxl/files/unique_fd.h"
#include "lib/fxl/portable_unistd.h"
namespace blink {
bool DirectoryAssetBundle::GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) {
if (fd_.is_valid()) {
#if defined(OS_WIN)
// This code path is not valid in a Windows environment.
return false;
#else
fxl::UniqueFD asset_file(openat(fd_.get(), asset_name.c_str(), O_RDONLY));
if (!asset_file.is_valid())
return false;
constexpr size_t kBufferSize = 1 << 16;
size_t offset = 0;
ssize_t bytes_read = 0;
do {
offset += bytes_read;
data->resize(offset + kBufferSize);
bytes_read = read(asset_file.get(), &(*data)[offset], kBufferSize);
} while (bytes_read > 0);
DirectoryAssetBundle::DirectoryAssetBundle(fml::UniqueFD descriptor)
: descriptor_(std::move(descriptor)) {
if (!fml::IsDirectory(descriptor_)) {
return;
}
is_valid_ = true;
}
if (bytes_read < 0) {
FXL_LOG(ERROR) << "Reading " << asset_name << " failed";
data->clear();
return false;
}
DirectoryAssetBundle::~DirectoryAssetBundle() = default;
data->resize(offset + bytes_read);
return true;
#endif
}
std::string asset_path = GetPathForAsset(asset_name);
if (asset_path.empty())
return false;
return files::ReadFileToVector(asset_path, data);
// |blink::AssetResolver|
bool DirectoryAssetBundle::IsValid() const {
return is_valid_;
}
DirectoryAssetBundle::~DirectoryAssetBundle() {}
// |blink::AssetResolver|
bool DirectoryAssetBundle::GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) const {
if (data == nullptr) {
return false;
}
DirectoryAssetBundle::DirectoryAssetBundle(std::string directory)
: directory_(std::move(directory)), fd_() {}
if (!is_valid_) {
FXL_DLOG(WARNING) << "Asset bundle was not valid.";
return false;
}
DirectoryAssetBundle::DirectoryAssetBundle(fxl::UniqueFD fd)
: fd_(std::move(fd)) {}
fml::FileMapping mapping(
fml::OpenFile(descriptor_, asset_name.c_str(), fml::OpenPermission::kRead,
false /* directory */),
false /* executable */);
std::string DirectoryAssetBundle::GetPathForAsset(
const std::string& asset_name) {
std::string asset_path = files::SimplifyPath(directory_ + "/" + asset_name);
if (asset_path.find(directory_) != 0u) {
FXL_LOG(ERROR) << "Asset name '" << asset_name
<< "' attempted to traverse outside asset bundle.";
return std::string();
if (mapping.GetMapping() == nullptr) {
return false;
}
return asset_path;
data->resize(mapping.GetSize());
memmove(data->data(), mapping.GetMapping(), mapping.GetSize());
return true;
}
} // namespace blink
......@@ -5,31 +5,31 @@
#ifndef FLUTTER_ASSETS_DIRECTORY_ASSET_BUNDLE_H_
#define FLUTTER_ASSETS_DIRECTORY_ASSET_BUNDLE_H_
#include <string>
#include <vector>
#include "flutter/assets/asset_provider.h"
#include "lib/fxl/files/unique_fd.h"
#include "flutter/assets/asset_resolver.h"
#include "flutter/fml/unique_fd.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/ref_counted.h"
namespace blink {
class DirectoryAssetBundle
: public AssetProvider {
class DirectoryAssetBundle : public AssetResolver {
public:
explicit DirectoryAssetBundle(std::string directory);
// Expects fd to be valid, otherwise the file descriptor is ignored.
explicit DirectoryAssetBundle(fxl::UniqueFD fd);
virtual ~DirectoryAssetBundle();
virtual bool GetAsBuffer(const std::string& asset_name, std::vector<uint8_t>* data);
explicit DirectoryAssetBundle(fml::UniqueFD descriptor);
std::string GetPathForAsset(const std::string& asset_name);
~DirectoryAssetBundle() override;
private:
const std::string directory_;
fxl::UniqueFD fd_;
const fml::UniqueFD descriptor_;
bool is_valid_ = false;
std::string GetPathForAsset(const std::string& asset_name) const;
// |blink::AssetResolver|
bool IsValid() const override;
// |blink::AssetResolver|
bool GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) const override;
FXL_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle);
};
......
// Copyright 2016 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/assets/unzipper_provider.h"
#include "lib/fxl/logging.h"
#include "third_party/zlib/contrib/minizip/unzip.h"
namespace blink {
UnzipperProvider GetUnzipperProviderForPath(std::string zip_path) {
return [zip_path]() {
zip::UniqueUnzipper unzipper(unzOpen2(zip_path.c_str(), nullptr));
if (!unzipper.is_valid())
FXL_LOG(ERROR) << "Unable to open zip file: " << zip_path;
return unzipper;
};
}
} // namespace blink
// Copyright 2016 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.
#ifndef FLUTTER_ASSETS_UNZIP_PROVIDER_H_
#define FLUTTER_ASSETS_UNZIP_PROVIDER_H_
#include <functional>
#include "lib/zip/unique_unzipper.h"
namespace blink {
using UnzipperProvider = std::function<zip::UniqueUnzipper()>;
UnzipperProvider GetUnzipperProviderForPath(std::string zip_path);
} // namespace blink
#endif // FLUTTER_ASSETS_UNZIP_PROVIDER_H_
......@@ -15,21 +15,28 @@
#include <utility>
#include "flutter/glue/trace_event.h"
#include "lib/fxl/files/eintr_wrapper.h"
#include "lib/fxl/files/unique_fd.h"
#include "lib/zip/unique_unzipper.h"
namespace blink {
ZipAssetStore::ZipAssetStore(UnzipperProvider unzipper_provider)
: unzipper_provider_(std::move(unzipper_provider)) {
ZipAssetStore::ZipAssetStore(std::string file_path)
: file_path_(std::move(file_path)) {
BuildStatCache();
}
ZipAssetStore::~ZipAssetStore() = default;
zip::UniqueUnzipper ZipAssetStore::CreateUnzipper() const {
return zip::UniqueUnzipper{::unzOpen2(file_path_.c_str(), nullptr)};
}
// |blink::AssetResolver|
bool ZipAssetStore::IsValid() const {
return stat_cache_.size() > 0;
}
// |blink::AssetResolver|
bool ZipAssetStore::GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) {
std::vector<uint8_t>* data) const {
TRACE_EVENT0("flutter", "ZipAssetStore::GetAsBuffer");
auto found = stat_cache_.find(asset_name);
......@@ -37,7 +44,7 @@ bool ZipAssetStore::GetAsBuffer(const std::string& asset_name,
return false;
}
auto unzipper = unzipper_provider_();
auto unzipper = CreateUnzipper();
if (!unzipper.is_valid()) {
return false;
......@@ -73,7 +80,8 @@ bool ZipAssetStore::GetAsBuffer(const std::string& asset_name,
void ZipAssetStore::BuildStatCache() {
TRACE_EVENT0("flutter", "ZipAssetStore::BuildStatCache");
auto unzipper = unzipper_provider_();
auto unzipper = CreateUnzipper();
if (!unzipper.is_valid()) {
return;
......
......@@ -6,21 +6,20 @@
#define FLUTTER_ASSETS_ZIP_ASSET_STORE_H_
#include <map>
#include <vector>
#include "flutter/assets/unzipper_provider.h"
#include "flutter/assets/asset_resolver.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/ref_counted.h"
#include "lib/zip/unique_unzipper.h"
#include "third_party/zlib/contrib/minizip/unzip.h"
namespace blink {
class ZipAssetStore : public fxl::RefCountedThreadSafe<ZipAssetStore> {
class ZipAssetStore final : public AssetResolver {
public:
explicit ZipAssetStore(UnzipperProvider unzipper_provider);
~ZipAssetStore();
ZipAssetStore(std::string file_path);
bool GetAsBuffer(const std::string& asset_name, std::vector<uint8_t>* data);
~ZipAssetStore() override;
private:
struct CacheEntry {
......@@ -30,11 +29,20 @@ class ZipAssetStore : public fxl::RefCountedThreadSafe<ZipAssetStore> {
: file_pos(p_file_pos), uncompressed_size(p_uncompressed_size) {}
};
UnzipperProvider unzipper_provider_;
std::map<std::string, CacheEntry> stat_cache_;
std::string file_path_;
mutable std::map<std::string, CacheEntry> stat_cache_;
// |blink::AssetResolver|
bool IsValid() const override;
// |blink::AssetResolver|
bool GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) const override;
void BuildStatCache();
zip::UniqueUnzipper CreateUnzipper() const;
FXL_DISALLOW_COPY_AND_ASSIGN(ZipAssetStore);
};
......
......@@ -12,11 +12,12 @@ source_set("common") {
sources = [
"settings.cc",
"settings.h",
"threads.cc",
"threads.h",
"task_runners.cc",
"task_runners.h",
]
deps = [
"$flutter_root/fml",
"//garnet/public/lib/fxl",
]
......
......@@ -4,26 +4,50 @@
#include "flutter/common/settings.h"
#include <memory>
#include "lib/fxl/logging.h"
#include <sstream>
namespace blink {
namespace {
Settings* g_settings = nullptr;
} // namespace
const Settings& Settings::Get() {
FXL_CHECK(g_settings);
return *g_settings;
}
void Settings::Set(const Settings& settings) {
FXL_CHECK(!g_settings);
g_settings = new Settings();
*g_settings = settings;
std::string Settings::ToString() const {
std::stringstream stream;
stream << "Settings: " << std::endl;
stream << "aot_snapshot_path: " << aot_snapshot_path << std::endl;
stream << "script_snapshot_path: " << script_snapshot_path << std::endl;
stream << "aot_vm_snapshot_data_filename: " << aot_vm_snapshot_data_filename
<< std::endl;
stream << "aot_vm_snapshot_instr_filename: " << aot_vm_snapshot_instr_filename
<< std::endl;
stream << "aot_isolate_snapshot_data_filename: "
<< aot_isolate_snapshot_data_filename << std::endl;
stream << "aot_isolate_snapshot_instr_filename: "
<< aot_isolate_snapshot_instr_filename << std::endl;
stream << "application_library_path: " << application_library_path
<< std::endl;
stream << "main_dart_file_path: " << main_dart_file_path << std::endl;
stream << "packages_file_path: " << packages_file_path << std::endl;
stream << "temp_directory_path: " << temp_directory_path << std::endl;
stream << "dart_flags:" << std::endl;
for (const auto& dart_flag : dart_flags) {
stream << " " << dart_flag << std::endl;
}
stream << "start_paused: " << start_paused << std::endl;
stream << "trace_skia: " << trace_skia << std::endl;
stream << "trace_startup: " << trace_startup << std::endl;
stream << "endless_trace_buffer: " << endless_trace_buffer << std::endl;
stream << "enable_dart_profiling: " << enable_dart_profiling << std::endl;
stream << "dart_non_checked_mode: " << dart_non_checked_mode << std::endl;
stream << "enable_observatory: " << enable_observatory << std::endl;
stream << "observatory_port: " << observatory_port << std::endl;
stream << "ipv6: " << ipv6 << std::endl;
stream << "use_test_fonts: " << use_test_fonts << std::endl;
stream << "enable_software_rendering: " << enable_software_rendering
<< std::endl;
stream << "using_blink: " << using_blink << std::endl;
stream << "log_tag: " << log_tag << std::endl;
stream << "icu_data_path: " << icu_data_path << std::endl;
stream << "assets_dir: " << assets_dir << std::endl;
stream << "assets_path: " << assets_path << std::endl;
return stream.str();
}
} // namespace blink
......@@ -5,40 +5,82 @@
#ifndef FLUTTER_COMMON_SETTINGS_H_
#define FLUTTER_COMMON_SETTINGS_H_
#include <fcntl.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include "flutter/fml/unique_fd.h"
#include "lib/fxl/functional/closure.h"
namespace blink {
using TaskObserverAdd =
std::function<void(intptr_t /* key */, fxl::Closure /* callback */)>;
using TaskObserverRemove = std::function<void(intptr_t /* key */)>;
struct Settings {
bool enable_observatory = false;
// Port on target will be auto selected by the OS. A message will be printed
// on the target with the port after it has been selected.
uint32_t observatory_port = 0;
bool ipv6 = false;
bool start_paused = false;
bool trace_startup = false;
bool endless_trace_buffer = false;
bool enable_dart_profiling = false;
bool use_test_fonts = false;
bool dart_non_checked_mode = false;
bool enable_software_rendering = false;
bool using_blink = true;
std::string aot_shared_library_path;
// VM settings
std::string script_snapshot_path;
std::string kernel_snapshot_path;
std::string aot_snapshot_path;
std::string aot_vm_snapshot_data_filename;
std::string aot_vm_snapshot_instr_filename;
std::string aot_isolate_snapshot_data_filename;
std::string aot_isolate_snapshot_instr_filename;
std::string application_library_path;
std::string application_kernel_asset;
std::string main_dart_file_path;
std::string packages_file_path;
std::string temp_directory_path;
std::vector<std::string> dart_flags;
// Isolate settings
bool start_paused = false;
bool trace_skia = false;
bool trace_startup = false;
bool endless_trace_buffer = false;
bool enable_dart_profiling = false;
bool dart_non_checked_mode = false;
// Observatory settings
bool enable_observatory = false;
// Port on target will be auto selected by the OS. A message will be printed
// on the target with the port after it has been selected.
uint32_t observatory_port = 0;
bool ipv6 = false;
// Font settings
bool use_test_fonts = false;
// Engine settings
TaskObserverAdd task_observer_add;
TaskObserverRemove task_observer_remove;
// The main isolate is current when this callback is made. This is a good spot
// to perform native Dart bindings for libraries not built in.
fxl::Closure root_isolate_create_callback;
// The isolate is not current and may have already been destroyed when this
// call is made.
fxl::Closure root_isolate_shutdown_callback;
bool enable_software_rendering = false;
bool using_blink = false;
bool skia_deterministic_rendering_on_cpu = false;
std::string log_tag = "flutter";
std::string icu_data_path;
// Assets settings
fml::UniqueFD::element_type assets_dir =
fml::UniqueFD::traits_type::InvalidValue();
std::string assets_path;
std::string flx_path;
static const Settings& Get();
static void Set(const Settings& settings);
std::string ToString() const;
};
} // namespace blink
......
// Copyright 2017 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/common/task_runners.h"
#include <utility>
namespace blink {
TaskRunners::TaskRunners(std::string label,
fxl::RefPtr<fxl::TaskRunner> platform,
fxl::RefPtr<fxl::TaskRunner> gpu,
fxl::RefPtr<fxl::TaskRunner> ui,
fxl::RefPtr<fxl::TaskRunner> io)
: label_(std::move(label)),
platform_(std::move(platform)),
gpu_(std::move(gpu)),
ui_(std::move(ui)),
io_(std::move(io)) {}
TaskRunners::~TaskRunners() = default;
const std::string& TaskRunners::GetLabel() const {
return label_;
}
fxl::RefPtr<fxl::TaskRunner> TaskRunners::GetPlatformTaskRunner() const {
return platform_;
}
fxl::RefPtr<fxl::TaskRunner> TaskRunners::GetUITaskRunner() const {
return ui_;
}
fxl::RefPtr<fxl::TaskRunner> TaskRunners::GetIOTaskRunner() const {
return io_;
}
fxl::RefPtr<fxl::TaskRunner> TaskRunners::GetGPUTaskRunner() const {
return gpu_;
}
bool TaskRunners::IsValid() const {
return platform_ && gpu_ && ui_ && io_;
}
} // namespace blink
// Copyright 2017 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.
#ifndef FLUTTER_COMMON_TASK_RUNNERS_H_
#define FLUTTER_COMMON_TASK_RUNNERS_H_
#include <string>
#include "lib/fxl/macros.h"
#include "lib/fxl/tasks/task_runner.h"
namespace blink {
class TaskRunners {
public:
TaskRunners(std::string label,
fxl::RefPtr<fxl::TaskRunner> platform,
fxl::RefPtr<fxl::TaskRunner> gpu,
fxl::RefPtr<fxl::TaskRunner> ui,
fxl::RefPtr<fxl::TaskRunner> io);
~TaskRunners();
const std::string& GetLabel() const;
fxl::RefPtr<fxl::TaskRunner> GetPlatformTaskRunner() const;
fxl::RefPtr<fxl::TaskRunner> GetUITaskRunner() const;
fxl::RefPtr<fxl::TaskRunner> GetIOTaskRunner() const;
fxl::RefPtr<fxl::TaskRunner> GetGPUTaskRunner() const;
bool IsValid() const;
private:
const std::string label_;
fxl::RefPtr<fxl::TaskRunner> platform_;
fxl::RefPtr<fxl::TaskRunner> gpu_;
fxl::RefPtr<fxl::TaskRunner> ui_;
fxl::RefPtr<fxl::TaskRunner> io_;
};
} // namespace blink
#endif // FLUTTER_COMMON_TASK_RUNNERS_H_
// Copyright 2016 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/common/threads.h"
#include <utility>
namespace blink {
namespace {
Threads* g_threads = nullptr;
} // namespace
Threads::Threads() {}
Threads::Threads(fxl::RefPtr<fxl::TaskRunner> platform,
fxl::RefPtr<fxl::TaskRunner> gpu,
fxl::RefPtr<fxl::TaskRunner> ui,
fxl::RefPtr<fxl::TaskRunner> io)
: platform_(std::move(platform)),
gpu_(std::move(gpu)),
ui_(std::move(ui)),
io_(std::move(io)) {}
Threads::~Threads() {}
const fxl::RefPtr<fxl::TaskRunner>& Threads::Platform() {
return Get().platform_;
}
const fxl::RefPtr<fxl::TaskRunner>& Threads::Gpu() {
return Get().gpu_;
}
const fxl::RefPtr<fxl::TaskRunner>& Threads::UI() {
return Get().ui_;
}
const fxl::RefPtr<fxl::TaskRunner>& Threads::IO() {
return Get().io_;
}
const Threads& Threads::Get() {
FXL_CHECK(g_threads);
return *g_threads;
}
void Threads::Set(const Threads& threads) {
FXL_CHECK(!g_threads);
g_threads = new Threads();
*g_threads = threads;
}
} // namespace blink
// Copyright 2016 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.
#ifndef FLUTTER_COMMON_THREADS_H_
#define FLUTTER_COMMON_THREADS_H_
#include "lib/fxl/tasks/task_runner.h"
#define ASSERT_IS_PLATFORM_THREAD \
FXL_DCHECK(::blink::Threads::Platform()->RunsTasksOnCurrentThread());
#define ASSERT_IS_GPU_THREAD \
FXL_DCHECK(::blink::Threads::Gpu()->RunsTasksOnCurrentThread());
#define ASSERT_IS_UI_THREAD \
FXL_DCHECK(::blink::Threads::UI()->RunsTasksOnCurrentThread());
#define ASSERT_IS_IO_THREAD \
FXL_DCHECK(::blink::Threads::IO()->RunsTasksOnCurrentThread());
namespace blink {
class Threads {
public:
Threads();
Threads(fxl::RefPtr<fxl::TaskRunner> platform,
fxl::RefPtr<fxl::TaskRunner> gpu,
fxl::RefPtr<fxl::TaskRunner> ui,
fxl::RefPtr<fxl::TaskRunner> io);
~Threads();
static const fxl::RefPtr<fxl::TaskRunner>& Platform();
static const fxl::RefPtr<fxl::TaskRunner>& Gpu();
static const fxl::RefPtr<fxl::TaskRunner>& UI();
static const fxl::RefPtr<fxl::TaskRunner>& IO();
static void Set(const Threads& settings);
private:
static const Threads& Get();
fxl::RefPtr<fxl::TaskRunner> platform_;
fxl::RefPtr<fxl::TaskRunner> gpu_;
fxl::RefPtr<fxl::TaskRunner> ui_;
fxl::RefPtr<fxl::TaskRunner> io_;
};
} // namespace blink
#endif // FLUTTER_COMMON_THREADS_H_
......@@ -20,23 +20,28 @@ template("flutter_content_handler") {
sources = [
"accessibility_bridge.cc",
"accessibility_bridge.h",
"app.cc",
"app.h",
"application_controller_impl.cc",
"application_controller_impl.h",
"application.cc",
"application.h",
"application_runner.cc",
"application_runner.h",
"compositor_context.cc",
"compositor_context.h",
"engine.cc",
"engine.h",
"fuchsia_font_manager.cc",
"fuchsia_font_manager.h",
"isolate_configurator.cc",
"isolate_configurator.h",
"main.cc",
"rasterizer.cc",
"rasterizer.h",
"runtime_holder.cc",
"runtime_holder.h",
"service_protocol_hooks.cc",
"service_protocol_hooks.h",
"platform_view.cc",
"platform_view.h",
"session_connection.cc",
"session_connection.h",
"vulkan_rasterizer.cc",
"vulkan_rasterizer.h",
"surface.cc",
"surface.h",
"task_observers.cc",
"task_observers.h",
"unique_fdio_ns.h",
"vulkan_surface.cc",
"vulkan_surface.h",
"vulkan_surface_pool.cc",
......@@ -45,18 +50,26 @@ template("flutter_content_handler") {
"vulkan_surface_producer.h",
]
# The use of these dependencies is temporary and will be moved behind the
# embedder API.
flutter_deps = [
"$flutter_root/assets",
"$flutter_root/common",
"$flutter_root/flow",
"$flutter_root/glue",
"$flutter_root/lib/ui",
"$flutter_root/runtime",
"$flutter_root/sky/engine/platform",
"$flutter_root/third_party/txt",
"$flutter_root/vulkan",
"$flutter_root/fml",
"$flutter_root/shell/common",
"$flutter_root/shell/gpu",
]
deps = [
"//third_party/dart/runtime/bin:libdart_builtin",
"//third_party/dart/runtime/platform:libdart_platform",
"$flutter_root/assets",
"$flutter_root/common",
"$flutter_root/flow",
"$flutter_root/glue",
"$flutter_root/lib/ui",
"$flutter_root/runtime",
"$flutter_root/sky/engine/platform",
"$flutter_root/third_party/txt",
"$flutter_root/vulkan",
"//garnet/public/lib/app/cpp",
"//garnet/public/lib/fsl",
"//garnet/public/lib/fxl",
......@@ -73,8 +86,10 @@ template("flutter_content_handler") {
"//topaz/lib/tonic",
"//topaz/public/dart-pkg/fuchsia",
"//topaz/public/lib/ui/flutter/sdk_ext",
"//third_party/skia:gpu",
"//third_party/zlib:minizip",
"//zircon/public/lib/trace-provider",
] + extra_deps
] + extra_deps + flutter_deps
# The flags below are needed so that Dart's CPU profiler can walk the
# C++ stack.
......
Flutter Application Runner
==========================
Implements the `component::ApplicationRunner` FIDL interface to launch and run mutliple Flutter applications within the same process.
......@@ -2,21 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/content_handler/accessibility_bridge.h"
#include "accessibility_bridge.h"
#include <unordered_set>
#include "flutter/lib/ui/semantics/semantics_node.h"
#include "lib/app/cpp/application_context.h"
#include "lib/fxl/macros.h"
#include "lib/context/fidl/context_writer.fidl.h"
#include "third_party/rapidjson/rapidjson/document.h"
#include "third_party/rapidjson/rapidjson/stringbuffer.h"
#include "third_party/rapidjson/rapidjson/writer.h"
namespace flutter_runner {
namespace flutter {
AccessibilityBridge::AccessibilityBridge(component::ApplicationContext* context)
: writer_(context->ConnectToEnvironmentService<modular::ContextWriter>()) {}
AccessibilityBridge::AccessibilityBridge(maxwell::ContextWriterPtr writer)
: writer_(std::move(writer)) {}
AccessibilityBridge::~AccessibilityBridge() = default;
void AccessibilityBridge::UpdateSemantics(
const blink::SemanticsNodeUpdates& update) {
......@@ -77,4 +78,4 @@ void AccessibilityBridge::EraseUnvisitedNodes(
}
}
} // namespace flutter_runner
} // namespace flutter
......@@ -2,28 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_CONTENT_HANDLER_ACCESSIBILITY_BRIDGE_H_
#define FLUTTER_CONTENT_HANDLER_ACCESSIBILITY_BRIDGE_H_
#pragma once
#include <map>
#include "flutter/lib/ui/semantics/semantics_node.h"
#include "lib/app/cpp/application_context.h"
#include <fuchsia/cpp/modular.h>
#include "lib/context/fidl/context_writer.fidl.h"
#include "lib/fxl/macros.h"
namespace flutter_runner {
namespace flutter {
// Maintain an up-to-date list of SemanticsNodes on screen, and communicate
// with the Context Service.
class AccessibilityBridge {
class AccessibilityBridge final {
public:
explicit AccessibilityBridge(component::ApplicationContext* context);
AccessibilityBridge(maxwell::ContextWriterPtr writer);
~AccessibilityBridge();
// Update the internal representation of the semantics nodes, and write the
// semantics to Context Service.
void UpdateSemantics(const blink::SemanticsNodeUpdates& update);
private:
maxwell::ContextWriterPtr writer_;
std::map<int, blink::SemanticsNode> semantics_nodes_;
// Walk the semantics node tree starting at |id|, and store the id of each
// visited child in |visited_nodes|.
void UpdateVisitedForNodeAndChildren(const int id,
......@@ -33,10 +37,7 @@ class AccessibilityBridge {
// |visited_nodes|.
void EraseUnvisitedNodes(const std::vector<int>& visited_nodes);
std::map<int, blink::SemanticsNode> semantics_nodes_;
modular::ContextWriterPtr writer_;
FXL_DISALLOW_COPY_AND_ASSIGN(AccessibilityBridge);
};
} // namespace flutter_runner
#endif // FLUTTER_CONTENT_HANDLER_ACCESSIBILITY_BRIDGE_H_
} // namespace flutter
// Copyright 2016 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/content_handler/app.h"
#include <thread>
#include <utility>
#include "flutter/common/settings.h"
#include "flutter/common/threads.h"
#include "flutter/content_handler/fuchsia_font_manager.h"
#include "flutter/lib/ui/text/font_collection.h"
#include "flutter/sky/engine/platform/fonts/fuchsia/FontCacheFuchsia.h"
#include "lib/fsl/tasks/message_loop.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/tasks/task_runner.h"
#include "lib/icu_data/cpp/icu_data.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
namespace flutter_runner {
namespace {
static App* g_app = nullptr;
void QuitMessageLoop() {
fsl::MessageLoop::GetCurrent()->QuitNow();
}
std::string GetLabelFromURL(const std::string& url) {
size_t last_slash = url.rfind('/');
if (last_slash == std::string::npos || last_slash + 1 == url.length())
return url;
return url.substr(last_slash + 1);
}
} // namespace
App::App() {
g_app = this;
context_ = component::ApplicationContext::CreateFromStartupInfo();
gpu_thread_ = std::make_unique<fsl::Thread>();
io_thread_ = std::make_unique<fsl::Thread>();
auto gpu_thread_success = gpu_thread_->Run();
auto io_thread_success = io_thread_->Run();
FXL_CHECK(gpu_thread_success) << "Must be able to create the GPU thread";
FXL_CHECK(io_thread_success) << "Must be able to create the IO thread";
auto ui_task_runner = fsl::MessageLoop::GetCurrent()->task_runner();
auto gpu_task_runner = gpu_thread_->TaskRunner();
auto io_task_runner = io_thread_->TaskRunner();
// Notice that the Platform and UI threads are actually the same.
blink::Threads::Set(blink::Threads(ui_task_runner, // Platform
gpu_task_runner, // GPU
ui_task_runner, // UI
io_task_runner // IO
));
if (!icu_data::Initialize(context_.get())) {
FXL_LOG(ERROR) << "Could not initialize ICU data.";
}
blink::Settings settings;
settings.enable_observatory = true;
blink::Settings::Set(settings);
fonts::FontProviderPtr font_provider(
context_->ConnectToEnvironmentService<fonts::FontProvider>());
if (settings.using_blink) {
blink::SetFontProvider(std::move(font_provider));
} else {
blink::FontCollection::ForProcess().GetFontCollection()->
SetAssetFontManager(
sk_make_sp<txt::FuchsiaFontManager>(std::move(font_provider)));
}
context_->outgoing_services()->AddService<component::ApplicationRunner>(
[this](fidl::InterfaceRequest<component::ApplicationRunner> request) {
runner_bindings_.AddBinding(this, std::move(request));
});
}
App::~App() {
icu_data::Release();
blink::Threads::Gpu()->PostTask(QuitMessageLoop);
blink::Threads::IO()->PostTask(QuitMessageLoop);
g_app = nullptr;
}
App& App::Shared() {
FXL_DCHECK(g_app);
return *g_app;
}
void App::WaitForPlatformViewIds(
std::vector<PlatformViewInfo>* platform_view_ids) {
fxl::AutoResetWaitableEvent latch;
blink::Threads::UI()->PostTask([this, platform_view_ids, &latch]() {
WaitForPlatformViewsIdsUIThread(platform_view_ids, &latch);
});
latch.Wait();
}
void App::WaitForPlatformViewsIdsUIThread(
std::vector<PlatformViewInfo>* platform_view_ids,
fxl::AutoResetWaitableEvent* latch) {
for (auto it = controllers_.begin(); it != controllers_.end(); it++) {
ApplicationControllerImpl* controller = it->first;
if (!controller) {
continue;
}
PlatformViewInfo info;
// TODO(zra): We should create real IDs for these instead of relying on the
// address of the controller. Maybe just use the UI Isolate main port?
info.view_id = reinterpret_cast<uintptr_t>(controller);
info.isolate_id = controller->GetUIIsolateMainPort();
info.isolate_name = controller->GetUIIsolateName();
platform_view_ids->push_back(info);
}
latch->Signal();
}
void App::StartApplication(
component::ApplicationPackage application,
component::ApplicationStartupInfo startup_info,
fidl::InterfaceRequest<component::ApplicationController> controller) {
if (controllers_.empty()) {
// Name this process after the url of the first application being launched.
base_label_ = "flutter:" + GetLabelFromURL(startup_info.launch_info.url);
}
std::unique_ptr<ApplicationControllerImpl> impl =
std::make_unique<ApplicationControllerImpl>(this, std::move(application),
std::move(startup_info),
std::move(controller));
ApplicationControllerImpl* key = impl.get();
controllers_.emplace(key, std::move(impl));
UpdateProcessLabel();
}
void App::Destroy(ApplicationControllerImpl* controller) {
auto it = controllers_.find(controller);
if (it == controllers_.end())
return;
controllers_.erase(it);
UpdateProcessLabel();
}
void App::UpdateProcessLabel() {
std::string label;
if (controllers_.size() < 2) {
label = base_label_;
} else {
std::string suffix = " (+" + std::to_string(controllers_.size() - 1) + ")";
if (base_label_.size() + suffix.size() <= ZX_MAX_NAME_LEN - 1) {
label = base_label_ + suffix;
} else {
label = base_label_.substr(0, ZX_MAX_NAME_LEN - 1 - suffix.size() - 3) +
"..." + suffix;
}
}
zx::process::self().set_property(ZX_PROP_NAME, label.c_str(), label.size());
}
} // namespace flutter_runner
// Copyright 2016 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.
#ifndef FLUTTER_CONTENT_HANDLER_APP_H_
#define FLUTTER_CONTENT_HANDLER_APP_H_
#include <memory>
#include <unordered_set>
#include "flutter/content_handler/application_controller_impl.h"
#include "lib/app/cpp/application_context.h"
#include <fuchsia/cpp/component.h>
#include "lib/fsl/threading/thread.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/synchronization/waitable_event.h"
namespace flutter_runner {
class App : public component::ApplicationRunner {
public:
App();
~App();
static App& Shared();
// |component::ApplicationRunner| implementation:
void StartApplication(
component::ApplicationPackage application,
component::ApplicationStartupInfo startup_info,
fidl::InterfaceRequest<component::ApplicationController> controller) override;
void Destroy(ApplicationControllerImpl* controller);
struct PlatformViewInfo {
uintptr_t view_id;
int64_t isolate_id;
std::string isolate_name;
};
void WaitForPlatformViewIds(std::vector<PlatformViewInfo>* platform_view_ids);
private:
void WaitForPlatformViewsIdsUIThread(
std::vector<PlatformViewInfo>* platform_view_ids,
fxl::AutoResetWaitableEvent* latch);
void UpdateProcessLabel();
std::unique_ptr<component::ApplicationContext> context_;
std::unique_ptr<fsl::Thread> gpu_thread_;
std::unique_ptr<fsl::Thread> io_thread_;
fidl::BindingSet<component::ApplicationRunner> runner_bindings_;
std::unordered_map<ApplicationControllerImpl*,
std::unique_ptr<ApplicationControllerImpl>>
controllers_;
std::string base_label_;
FXL_DISALLOW_COPY_AND_ASSIGN(App);
};
} // namespace flutter_runner
#endif // FLUTTER_CONTENT_HANDLER_APP_H_
// Copyright 2018 The Fuchsia 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 "application.h"
#include <dlfcn.h>
#include <zircon/dlfcn.h>
#include <sstream>
#include "flutter/shell/common/switches.h"
#include "lib/fsl/vmo/file.h"
#include "lib/fsl/vmo/vector.h"
#include "lib/fxl/command_line.h"
#include "lib/fxl/synchronization/waitable_event.h"
#include "task_observers.h"
namespace flutter {
std::pair<std::unique_ptr<fsl::Thread>, std::unique_ptr<Application>>
Application::Create(
Application::Delegate& delegate,
component::ApplicationPackagePtr package,
component::ApplicationStartupInfoPtr startup_info,
f1dl::InterfaceRequest<component::ApplicationController> controller) {
auto thread = std::make_unique<fsl::Thread>();
std::unique_ptr<Application> application;
fxl::AutoResetWaitableEvent latch;
thread->TaskRunner()->PostTask([&]() mutable {
application.reset(new Application(delegate, //
std::move(package), //
std::move(startup_info), //
std::move(controller) //
));
latch.Signal();
});
thread->Run();
latch.Wait();
return {std::move(thread), std::move(application)};
}
static std::string DebugLabelForURL(const std::string url) {
auto found = url.rfind("/");
if (found == std::string::npos) {
return url;
} else {
return {url, found + 1};
}
}
Application::Application(
Application::Delegate& delegate,
component::ApplicationPackagePtr package,
component::ApplicationStartupInfoPtr startup_info,
f1dl::InterfaceRequest<component::ApplicationController>
application_controller_request)
: delegate_(delegate),
debug_label_(DebugLabelForURL(startup_info->launch_info->url)),
application_controller_(this) {
application_controller_.set_error_handler([this]() { Kill(); });
FXL_DCHECK(fdio_ns_.is_valid());
// ApplicationLaunchInfo::url non-optional.
auto& launch_info = startup_info->launch_info;
// ApplicationLaunchInfo::arguments optional.
if (auto& arguments = launch_info->arguments) {
settings_ = shell::SettingsFromCommandLine(
fxl::CommandLineFromIterators(arguments->begin(), arguments->end()));
}
// TODO: ApplicationLaunchInfo::out optional.
// TODO: ApplicationLaunchInfo::err optional.
// ApplicationLaunchInfo::service_request optional.
if (launch_info->directory_request) {
service_provider_bridge_.ServeDirectory(
std::move(launch_info->directory_request));
}
// ApplicationLaunchInfo::flat_namespace optional.
if (auto& flat_namespace = startup_info->flat_namespace) {
for (size_t i = 0; i < flat_namespace->paths->size(); ++i) {
const auto& path = flat_namespace->paths->at(i);
if (path == "/svc") {
continue;
}
zx::channel dir = std::move(flat_namespace->directories->at(i));
zx_handle_t dir_handle = dir.release();
if (fdio_ns_bind(fdio_ns_.get(), path->data(), dir_handle) != ZX_OK) {
FXL_DLOG(ERROR) << "Could not bind path to namespace: " << path;
zx_handle_close(dir_handle);
}
}
} else {
FXL_DLOG(ERROR) << "There was no flat namespace.";
}
application_directory_.reset(fdio_ns_opendir(fdio_ns_.get()));
FXL_DCHECK(application_directory_.is_valid());
application_assets_directory_.reset(
openat(application_directory_.get(), "pkg/data", O_RDONLY | O_DIRECTORY));
// TODO: ApplicationLaunchInfo::additional_services optional.
// ApplicationPackage::data: This is legacy FLX data. Ensure that we dont have
// any.
FXL_DCHECK(!package->data) << "Legacy FLX data must not be supplied.";
// All launch arguments have been read. Perform service binding and
// final settings configuration. The next call will be to create a view
// for this application.
service_provider_bridge_.AddService<mozart::ViewProvider>(
std::bind(&Application::CreateShellForView, this, std::placeholders::_1));
component::ServiceProviderPtr outgoing_services;
outgoing_services_request_ = outgoing_services.NewRequest();
service_provider_bridge_.set_backend(std::move(outgoing_services));
// Setup the application controller binding.
if (application_controller_request) {
application_controller_.Bind(std::move(application_controller_request));
}
application_context_ =
component::ApplicationContext::CreateFrom(std::move(startup_info));
settings_.enable_observatory = true;
settings_.icu_data_path = "";
settings_.using_blink = false;
settings_.assets_dir = application_assets_directory_.get();
settings_.script_snapshot_path = "snapshot_blob.bin";
settings_.log_tag = debug_label_ + std::string{"(flutter)"};
#ifndef NDEBUG
// Debug mode
settings_.dart_non_checked_mode = false;
#else // NDEBUG
// Release mode
settings_.dart_non_checked_mode = true;
#endif // NDEBUG
settings_.task_observer_add =
std::bind(&CurrentMessageLoopAddAfterTaskObserver, std::placeholders::_1,
std::placeholders::_2);
settings_.task_observer_remove = std::bind(
&CurrentMessageLoopRemoveAfterTaskObserver, std::placeholders::_1);
AttemptVMLaunchWithCurrentSettings(settings_);
}
Application::~Application() = default;
void Application::AttemptVMLaunchWithCurrentSettings(
const blink::Settings& settings) const {
if (blink::DartVM::ForProcessIfInitialized()) {
return;
}
if (!blink::DartVM::IsRunningPrecompiledCode()) {
// We will be initializing the VM lazily in this case.
return;
}
fsl::SizedVmo dylib_vmo;
if (!fsl::VmoFromFilenameAt(
application_assets_directory_.get() /* /pkg/data */, "libapp.so",
&dylib_vmo)) {
FXL_LOG(ERROR) << "Dylib containing VM and isolate snapshots does not "
"exist. Will not be able to launch VM.";
return;
}
dlerror();
auto library_handle = dlopen_vmo(dylib_vmo.vmo().get(), RTLD_LAZY);
if (library_handle == nullptr) {
FXL_LOG(ERROR) << "Could not open dylib: " << dlerror();
return;
}
auto lib = fxl::MakeRefCounted<fml::NativeLibrary>(
library_handle, // library handle
true // close the handle when done
);
auto symbol = [](const char* str) {
return std::string{"_"} + std::string{str};
};
fxl::RefPtr<blink::DartSnapshot> vm_snapshot =
fxl::MakeRefCounted<blink::DartSnapshot>(
blink::DartSnapshotBuffer::CreateWithSymbolInLibrary(
lib, symbol(blink::DartSnapshot::kVMDataSymbol).c_str()),
blink::DartSnapshotBuffer::CreateWithSymbolInLibrary(
lib, symbol(blink::DartSnapshot::kVMInstructionsSymbol).c_str()));
fxl::RefPtr<blink::DartSnapshot> isolate_snapshot =
fxl::MakeRefCounted<blink::DartSnapshot>(
blink::DartSnapshotBuffer::CreateWithSymbolInLibrary(
lib, symbol(blink::DartSnapshot::kIsolateDataSymbol).c_str()),
blink::DartSnapshotBuffer::CreateWithSymbolInLibrary(
lib,
symbol(blink::DartSnapshot::kIsolateInstructionsSymbol).c_str()));
blink::DartVM::ForProcess(settings_, //
std::move(vm_snapshot), //
std::move(isolate_snapshot) //
);
if (blink::DartVM::ForProcessIfInitialized()) {
FXL_DLOG(INFO) << "VM successfully initialized for AOT mode.";
} else {
FXL_LOG(ERROR) << "VM could not be initialized for AOT mode.";
}
}
// |component::ApplicationController|
void Application::Kill() {
if (last_return_code_.first) {
for (auto wait_callback : wait_callbacks_) {
wait_callback(last_return_code_.second);
}
}
wait_callbacks_.clear();
delegate_.OnApplicationTerminate(this);
// WARNING: Don't do anything past this point as this instance may have been
// collected.
}
// |component::ApplicationController|
void Application::Detach() {
application_controller_.set_error_handler(nullptr);
}
// |component::ApplicationController|
void Application::Wait(const WaitCallback& callback) {
wait_callbacks_.emplace_back(std::move(callback));
}
// |flutter::Engine::Delegate|
void Application::OnEngineTerminate(const Engine* shell_holder) {
auto found = std::find_if(shell_holders_.begin(), shell_holders_.end(),
[shell_holder](const auto& holder) {
return holder.get() == shell_holder;
});
if (found == shell_holders_.end()) {
return;
}
// We may launch multiple shell in this application. However, we will
// terminate when the last shell goes away. The error code return to the
// application controller will be the last isolate that had an error.
auto return_code = shell_holder->GetEngineReturnCode();
if (return_code.first) {
last_return_code_ = return_code;
}
shell_holders_.erase(found);
if (shell_holders_.size() == 0) {
Kill();
// WARNING: Don't do anything past this point because the delegate may have
// collected this instance via the termination callback.
}
}
void Application::CreateShellForView(
f1dl::InterfaceRequest<mozart::ViewProvider> view_provider_request) {
shells_bindings_.AddBinding(this, std::move(view_provider_request));
}
// |mozart::ViewProvider|
void Application::CreateView(
f1dl::InterfaceRequest<mozart::ViewOwner> view_owner,
f1dl::InterfaceRequest<component::ServiceProvider>) {
if (!application_context_) {
FXL_DLOG(ERROR) << "Application context was invalid when attempting to "
"create a shell for a view provider request.";
return;
}
// This method may be called multiple times. Care must be taken to ensure that
// all arguments can be accessed or synthesized multiple times.
// TODO(chinmaygarde): Figure out how to re-create the outgoing service
// request handle.
shell_holders_.emplace(std::make_unique<Engine>(
*this, // delegate
debug_label_, // thread label
*application_context_, // application context
settings_, // settings
std::move(view_owner), // view owner
fdio_ns_, // FDIO namespace
std::move(outgoing_services_request_) // outgoing request
));
}
} // namespace flutter
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <array>
#include <memory>
#include <set>
#include "engine.h"
#include "flutter/common/settings.h"
#include "lib/app/cpp/application_context.h"
#include "lib/app/fidl/application_controller.fidl.h"
#include "lib/fidl/cpp/bindings/binding_set.h"
#include "lib/fsl/threading/thread.h"
#include "lib/fxl/files/unique_fd.h"
#include "lib/fxl/macros.h"
#include "lib/svc/cpp/service_provider_bridge.h"
#include "lib/ui/views/fidl/view_provider.fidl.h"
#include "unique_fdio_ns.h"
namespace flutter {
// Represents an instance of a Flutter application that contains one of more
// Flutter engine instances.
class Application final : public Engine::Delegate,
public component::ApplicationController,
public mozart::ViewProvider {
public:
class Delegate {
public:
virtual void OnApplicationTerminate(const Application* application) = 0;
};
// Creates a dedicated thread to run the application and constructions the
// application on it. The application can be accessed only on this thread.
// This is a synchronous operation.
static std::pair<std::unique_ptr<fsl::Thread>, std::unique_ptr<Application>>
Create(Application::Delegate& delegate,
component::ApplicationPackagePtr package,
component::ApplicationStartupInfoPtr startup_info,
f1dl::InterfaceRequest<component::ApplicationController> controller);
// Must be called on the same thread returned from the create call. The thread
// may be collected after.
~Application();
private:
blink::Settings settings_;
Delegate& delegate_;
const std::string debug_label_;
UniqueFDIONS fdio_ns_ = UniqueFDIONSCreate();
fxl::UniqueFD application_directory_;
fxl::UniqueFD application_assets_directory_;
f1dl::Binding<component::ApplicationController> application_controller_;
f1dl::InterfaceRequest<component::ServiceProvider> outgoing_services_request_;
component::ServiceProviderBridge service_provider_bridge_;
std::unique_ptr<component::ApplicationContext> application_context_;
f1dl::BindingSet<mozart::ViewProvider> shells_bindings_;
std::set<std::unique_ptr<Engine>> shell_holders_;
std::vector<WaitCallback> wait_callbacks_;
std::pair<bool, uint32_t> last_return_code_;
Application(
Application::Delegate& delegate,
component::ApplicationPackagePtr package,
component::ApplicationStartupInfoPtr startup_info,
f1dl::InterfaceRequest<component::ApplicationController> controller);
// |component::ApplicationController|
void Kill() override;
// |component::ApplicationController|
void Detach() override;
// |component::ApplicationController|
void Wait(const WaitCallback& callback) override;
// |mozart::ViewProvider|
void CreateView(
f1dl::InterfaceRequest<mozart::ViewOwner> view_owner,
f1dl::InterfaceRequest<component::ServiceProvider> services) override;
// |flutter::Engine::Delegate|
void OnEngineTerminate(const Engine* holder) override;
void CreateShellForView(
f1dl::InterfaceRequest<mozart::ViewProvider> view_provider_request);
void AttemptVMLaunchWithCurrentSettings(
const blink::Settings& settings) const;
FXL_DISALLOW_COPY_AND_ASSIGN(Application);
};
} // namespace flutter
// Copyright 2016 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/content_handler/application_controller_impl.h"
#include <utility>
#include <fdio/namespace.h>
#include <zircon/status.h>
#include "flutter/content_handler/app.h"
#include "flutter/content_handler/runtime_holder.h"
#include "lib/app/cpp/connect.h"
#include "lib/fsl/vmo/vector.h"
#include "lib/fxl/logging.h"
namespace flutter_runner {
ApplicationControllerImpl::ApplicationControllerImpl(
App* app,
component::ApplicationPackage application,
component::ApplicationStartupInfo startup_info,
fidl::InterfaceRequest<component::ApplicationController> controller)
: app_(app), binding_(this) {
if (controller.is_valid()) {
binding_.Bind(std::move(controller));
binding_.set_error_handler([this] {
app_->Destroy(this);
// |this| has been deleted at this point.
});
}
std::vector<char> bundle;
if (application.data) {
if (!fsl::VectorFromVmo(std::move(*application.data), &bundle)) {
FXL_LOG(ERROR) << "Failed to receive bundle.";
return;
}
}
// TODO(jeffbrown): Decide what to do with command-line arguments and
// startup handles.
if (startup_info.launch_info.directory_request.is_valid()) {
service_provider_bridge_.ServeDirectory(
std::move(startup_info.launch_info.directory_request));
}
service_provider_bridge_.AddService<views_v1::ViewProvider>(
[this](fidl::InterfaceRequest<views_v1::ViewProvider> request) {
view_provider_bindings_.AddBinding(this, std::move(request));
});
component::ServiceProviderPtr service_provider;
auto request = service_provider.NewRequest();
service_provider_bridge_.set_backend(std::move(service_provider));
fdio_ns_t* fdio_ns = SetupNamespace(&startup_info.flat_namespace);
if (fdio_ns == nullptr) {
FXL_LOG(ERROR) << "Failed to initialize namespace";
return;
}
url_ = startup_info.launch_info.url;
runtime_holder_.reset(new RuntimeHolder());
runtime_holder_->SetMainIsolateShutdownCallback([this]() { Kill(); });
runtime_holder_->Init(
fdio_ns,
component::ApplicationContext::CreateFrom(std::move(startup_info)),
std::move(request), std::move(bundle));
}
ApplicationControllerImpl::~ApplicationControllerImpl() = default;
constexpr char kServiceRootPath[] = "/svc";
fdio_ns_t* ApplicationControllerImpl::SetupNamespace(
component::FlatNamespace* flat) {
fdio_ns_t* fdio_namespc;
zx_status_t status = fdio_ns_create(&fdio_namespc);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to create namespace";
return nullptr;
}
for (size_t i = 0; i < flat->paths->size(); ++i) {
if (flat->paths->at(i) == kServiceRootPath) {
// Ownership of /svc goes to the ApplicationContext created above.
continue;
}
zx::channel dir = std::move(flat->directories->at(i));
zx_handle_t dir_handle = dir.release();
const char* path = flat->paths->at(i)->data();
status = fdio_ns_bind(fdio_namespc, path, dir_handle);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failed to bind " << flat->paths->at(i)
<< " to namespace";
zx_handle_close(dir_handle);
fdio_ns_destroy(fdio_namespc);
return nullptr;
}
}
return fdio_namespc;
}
void ApplicationControllerImpl::Kill() {
SendReturnCode(runtime_holder_->return_code());
runtime_holder_.reset();
app_->Destroy(this);
// |this| has been deleted at this point.
}
void ApplicationControllerImpl::Detach() {
binding_.set_error_handler(fxl::Closure());
}
void ApplicationControllerImpl::Wait(WaitCallback callback) {
wait_callbacks_.push_back(std::move(callback));
}
void ApplicationControllerImpl::SendReturnCode(int32_t return_code) {
for (const auto& iter : wait_callbacks_) {
iter(return_code);
}
wait_callbacks_.clear();
}
void ApplicationControllerImpl::CreateView(
fidl::InterfaceRequest<views_v1_token::ViewOwner> view_owner_request,
fidl::InterfaceRequest<component::ServiceProvider> services) {
runtime_holder_->CreateView(url_, std::move(view_owner_request),
std::move(services));
}
Dart_Port ApplicationControllerImpl::GetUIIsolateMainPort() {
if (!runtime_holder_)
return ILLEGAL_PORT;
return runtime_holder_->GetUIIsolateMainPort();
}
std::string ApplicationControllerImpl::GetUIIsolateName() {
if (!runtime_holder_) {
return "";
}
return runtime_holder_->GetUIIsolateName();
}
} // namespace flutter_runner
// Copyright 2016 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.
#ifndef FLUTTER_CONTENT_HANDLER_APPLICATION_IMPL_H_
#define FLUTTER_CONTENT_HANDLER_APPLICATION_IMPL_H_
#include <memory>
#include <fdio/namespace.h>
#include <fuchsia/cpp/component.h>
#include <fuchsia/cpp/ui.h>
#include <fuchsia/cpp/views_v1.h>
#include "lib/fidl/cpp/binding.h"
#include "lib/fidl/cpp/binding_set.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/synchronization/waitable_event.h"
#include "lib/svc/cpp/service_provider_bridge.h"
#include "third_party/dart/runtime/include/dart_api.h"
namespace flutter_runner {
class App;
class RuntimeHolder;
class ApplicationControllerImpl : public component::ApplicationController,
public views_v1::ViewProvider {
public:
ApplicationControllerImpl(
App* app,
component::ApplicationPackage application,
component::ApplicationStartupInfo startup_info,
fidl::InterfaceRequest<component::ApplicationController> controller);
~ApplicationControllerImpl() override;
// |component::ApplicationController| implementation
void Kill() override;
void Detach() override;
void Wait(WaitCallback callback) override;
// |views_v1::ViewProvider| implementation
void CreateView(
fidl::InterfaceRequest<views_v1_token::ViewOwner> view_owner_request,
fidl::InterfaceRequest<component::ServiceProvider> services) override;
Dart_Port GetUIIsolateMainPort();
std::string GetUIIsolateName();
private:
void StartRuntimeIfReady();
void SendReturnCode(int32_t return_code);
fdio_ns_t* SetupNamespace(component::FlatNamespace* flat);
App* app_;
fidl::Binding<component::ApplicationController> binding_;
component::ServiceProviderBridge service_provider_bridge_;
fidl::BindingSet<views_v1::ViewProvider> view_provider_bindings_;
std::string url_;
std::unique_ptr<RuntimeHolder> runtime_holder_;
std::vector<WaitCallback> wait_callbacks_;
FXL_DISALLOW_COPY_AND_ASSIGN(ApplicationControllerImpl);
};
} // namespace flutter_runner
#endif // FLUTTER_CONTENT_HANDLER_APPLICATION_IMPL_H_
// Copyright 2018 The Fuchsia 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 "application_runner.h"
#include <utility>
#include "flutter/lib/ui/text/font_collection.h"
#include "fuchsia_font_manager.h"
#include "lib/fonts/fidl/font_provider.fidl.h"
#include "lib/icu_data/cpp/icu_data.h"
namespace flutter {
ApplicationRunner::ApplicationRunner(fxl::Closure on_termination_callback)
: on_termination_callback_(std::move(on_termination_callback)),
host_context_(component::ApplicationContext::CreateFromStartupInfo()) {
SetupICU();
SetupGlobalFonts();
const std::string process_label = "flutter";
zx::process::self().set_property(ZX_PROP_NAME, process_label.c_str(),
process_label.size());
host_context_->outgoing_services()->AddService<component::ApplicationRunner>(
std::bind(&ApplicationRunner::RegisterApplication, this,
std::placeholders::_1));
active_applications_bindings_.set_empty_set_handler(
[this]() { FireTerminationCallbackIfNecessary(); });
}
ApplicationRunner::~ApplicationRunner() {
host_context_->outgoing_services()
->RemoveService<component::ApplicationRunner>();
}
void ApplicationRunner::RegisterApplication(
f1dl::InterfaceRequest<component::ApplicationRunner> request) {
active_applications_bindings_.AddBinding(this, std::move(request));
}
void ApplicationRunner::StartApplication(
component::ApplicationPackagePtr package,
component::ApplicationStartupInfoPtr startup_info,
f1dl::InterfaceRequest<component::ApplicationController> controller) {
auto thread_application_pair =
Application::Create(*this, // delegate
std::move(package), // application pacakge
std::move(startup_info), // startup info
std::move(controller) // controller request
);
active_applications_[thread_application_pair.second.get()] =
std::move(thread_application_pair);
}
void ApplicationRunner::OnApplicationTerminate(const Application* application) {
active_applications_.erase(application);
FireTerminationCallbackIfNecessary();
}
void ApplicationRunner::SetupICU() {
if (!icu_data::Initialize(host_context_.get())) {
FXL_LOG(ERROR) << "Could not initialize ICU data.";
}
}
void ApplicationRunner::SetupGlobalFonts() {
fonts::FontProviderPtr font_provider(
host_context_->ConnectToEnvironmentService<fonts::FontProvider>());
auto font_manager =
sk_make_sp<txt::FuchsiaFontManager>(std::move(font_provider));
blink::FontCollection::ForProcess()
.GetFontCollection()
->SetDefaultFontManager(std::move(font_manager));
}
void ApplicationRunner::FireTerminationCallbackIfNecessary() {
// We have no reason to exist if:
// 1: No previously launched applications are running.
// 2: No bindings exist that may require launching more applications.
if (on_termination_callback_ && active_applications_.size() == 0 &&
active_applications_bindings_.size() == 0) {
on_termination_callback_();
}
}
} // namespace flutter
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <memory>
#include <unordered_map>
#include "application.h"
#include "lib/app/cpp/application_context.h"
#include "lib/app/fidl/application_runner.fidl.h"
#include "lib/fidl/cpp/bindings/binding_set.h"
#include "lib/fsl/tasks/message_loop.h"
#include "lib/fxl/functional/make_copyable.h"
#include "lib/fxl/macros.h"
namespace flutter {
// Publishes the |component::ApplicationRunner| service and runs applications on
// their own threads.
class ApplicationRunner final : public Application::Delegate,
public component::ApplicationRunner {
public:
ApplicationRunner(fxl::Closure on_termination_callback);
~ApplicationRunner();
private:
struct ActiveApplication {
std::unique_ptr<fsl::Thread> thread;
std::unique_ptr<Application> application;
ActiveApplication(std::pair<std::unique_ptr<fsl::Thread>,
std::unique_ptr<Application>> pair)
: thread(std::move(pair.first)), application(std::move(pair.second)) {}
ActiveApplication() {
if (thread && application) {
thread->TaskRunner()->PostTask(
fxl::MakeCopyable([application = std::move(application)]() mutable {
application.reset();
fsl::MessageLoop::GetCurrent()->PostQuitTask();
}));
thread.reset(); // join
}
}
};
fxl::Closure on_termination_callback_;
std::unique_ptr<component::ApplicationContext> host_context_;
f1dl::BindingSet<component::ApplicationRunner> active_applications_bindings_;
std::unordered_map<const Application*, ActiveApplication>
active_applications_;
// |component::ApplicationRunner|
void StartApplication(component::ApplicationPackagePtr application,
component::ApplicationStartupInfoPtr startup_info,
f1dl::InterfaceRequest<component::ApplicationController>
controller) override;
void RegisterApplication(
f1dl::InterfaceRequest<component::ApplicationRunner> request);
void UnregisterApplication(const Application* application);
// |Application::Delegate|
void OnApplicationTerminate(const Application* application) override;
void SetupICU();
void SetupGlobalFonts();
void FireTerminationCallbackIfNecessary();
FXL_DISALLOW_COPY_AND_ASSIGN(ApplicationRunner);
};
} // namespace flutter
// Copyright 2018 The Fuchsia 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 "compositor_context.h"
#include "flutter/flow/layers/layer_tree.h"
#include "flutter/glue/trace_event.h"
namespace flutter {
class ScopedFrame final : public flow::CompositorContext::ScopedFrame {
public:
ScopedFrame(flow::CompositorContext& context,
bool instrumentation_enabled,
SessionConnection& session_connection)
: flow::CompositorContext::ScopedFrame(context,
nullptr,
nullptr,
instrumentation_enabled),
session_connection_(session_connection) {}
private:
SessionConnection& session_connection_;
bool Raster(flow::LayerTree& layer_tree, bool ignore_raster_cache) override {
if (!session_connection_.has_metrics()) {
return true;
}
{
// Preroll the Flutter layer tree. This allows Flutter to perform
// pre-paint optimizations.
TRACE_EVENT0("flutter", "Preroll");
layer_tree.Preroll(*this, true /* ignore raster cache */);
}
{
// Traverse the Flutter layer tree so that the necessary session ops to
// represent the frame are enqueued in the underlying session.
TRACE_EVENT0("flutter", "UpdateScene");
layer_tree.UpdateScene(session_connection_.scene_update_context(),
session_connection_.root_node());
}
{
// Flush all pending session ops.
TRACE_EVENT0("flutter", "SessionPresent");
session_connection_.Present(*this);
}
return true;
}
FXL_DISALLOW_COPY_AND_ASSIGN(ScopedFrame);
};
CompositorContext::CompositorContext(
const ui::ScenicPtr& scenic,
std::string debug_label,
zx::eventpair import_token,
OnMetricsUpdate session_metrics_did_change_callback,
fxl::Closure session_error_callback)
: debug_label_(std::move(debug_label)),
session_connection_(scenic,
debug_label_,
std::move(import_token),
std::move(session_metrics_did_change_callback),
std::move(session_error_callback)) {}
CompositorContext::~CompositorContext() = default;
std::unique_ptr<flow::CompositorContext::ScopedFrame>
CompositorContext::AcquireFrame(GrContext* gr_context,
SkCanvas* canvas,
bool instrumentation_enabled) {
// TODO: The AcquireFrame interface is too broad and must be refactored to get
// rid of the context and canvas arguments as those seem to be only used for
// colorspace correctness purposes on the mobile shells.
return std::make_unique<flutter::ScopedFrame>(*this, //
instrumentation_enabled, //
session_connection_ //
);
}
} // namespace flutter
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include "flutter/flow/compositor_context.h"
#include "garnet/public/lib/ui/scenic/fidl/scenic.fidl-common.h"
#include "lib/fxl/macros.h"
#include "session_connection.h"
namespace flutter {
// Holds composition specific state and bindings specific to composition on
// Fuchsia.
class CompositorContext final : public flow::CompositorContext {
public:
CompositorContext(const ui::ScenicPtr& scenic,
std::string debug_label,
zx::eventpair import_token,
OnMetricsUpdate session_metrics_did_change_callback,
fxl::Closure session_error_callback);
~CompositorContext() override;
private:
const std::string debug_label_;
SessionConnection session_connection_;
// |flow::CompositorContext|
std::unique_ptr<ScopedFrame> AcquireFrame(
GrContext* gr_context,
SkCanvas* canvas,
bool instrumentation_enabled) override;
FXL_DISALLOW_COPY_AND_ASSIGN(CompositorContext);
};
} // namespace flutter
// Copyright 2018 The Fuchsia 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 "engine.h"
#include <sstream>
#include "flutter/common/task_runners.h"
#include "flutter/fml/task_runner.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/run_configuration.h"
#include "lib/fsl/tasks/message_loop.h"
#include "lib/fxl/functional/make_copyable.h"
#include "lib/fxl/synchronization/waitable_event.h"
#include "platform_view.h"
#ifdef ERROR
#undef ERROR
#endif
namespace flutter {
Engine::Engine(Delegate& delegate,
std::string thread_label,
component::ApplicationContext& application_context,
blink::Settings settings,
f1dl::InterfaceRequest<mozart::ViewOwner> view_owner,
const UniqueFDIONS& fdio_ns,
f1dl::InterfaceRequest<component::ServiceProvider>
outgoing_services_request)
: delegate_(delegate),
thread_label_(std::move(thread_label)),
settings_(std::move(settings)),
weak_factory_(this) {
// Launch the threads that will be used to run the shell. These threads will
// be joined in the destructor.
for (auto& thread : host_threads_) {
thread.Run();
}
mozart::ViewManagerPtr view_manager;
application_context.ConnectToEnvironmentService(view_manager.NewRequest());
zx::eventpair import_token, export_token;
if (zx::eventpair::create(0u, &import_token, &export_token) != ZX_OK) {
FXL_DLOG(ERROR) << "Could not create event pair.";
return;
}
// Setup the session connection.
ui::ScenicPtr scenic;
view_manager->GetScenic(scenic.NewRequest());
// Grab the parent environent services. The platform view may want to access
// some of these services.
component::ServiceProviderPtr parent_environment_service_provider;
application_context.environment()->GetServices(
parent_environment_service_provider.NewRequest());
// We need to manually schedule a frame when the session metrics change.
OnMetricsUpdate on_session_metrics_change_callback = std::bind(
&Engine::OnSessionMetricsDidChange, this, std::placeholders::_1);
fxl::Closure on_session_error_callback = std::bind(&Engine::Terminate, this);
// Grab the accessibilty context writer that can understand the semtics tree
// on the platform view.
maxwell::ContextWriterPtr accessibility_context_writer;
application_context.ConnectToEnvironmentService(
accessibility_context_writer.NewRequest());
// Setup the callback that will instantiate the platform view.
shell::Shell::CreateCallback<shell::PlatformView> on_create_platform_view =
fxl::MakeCopyable([debug_label = thread_label_, //
parent_environment_service_provider =
std::move(parent_environment_service_provider), //
view_manager = std::ref(view_manager), //
view_owner = std::move(view_owner), //
scenic = std::move(scenic), //
accessibility_context_writer =
std::move(accessibility_context_writer), //
export_token = std::move(export_token), //
import_token = std::move(import_token), //
on_session_metrics_change_callback, //
on_session_error_callback //
](shell::Shell& shell) mutable {
return std::make_unique<flutter::PlatformView>(
shell, // delegate
debug_label, // debug label
shell.GetTaskRunners(), // task runners
std::move(parent_environment_service_provider), // services
view_manager, // view manager
std::move(view_owner), // view owner
std::move(scenic), // scenic
std::move(export_token), // export token
std::move(import_token), // import token
std::move(
accessibility_context_writer), // accessibility context writer
std::move(on_session_metrics_change_callback), // metrics change
std::move(on_session_error_callback) // session_error
);
});
// Setup the callback that will instantiate the rasterizer.
shell::Shell::CreateCallback<shell::Rasterizer> on_create_rasterizer =
[](shell::Shell& shell) {
return std::make_unique<shell::Rasterizer>(
shell.GetTaskRunners() // task runners
);
};
// Get the task runners from the managed threads. The current thread will be
// used as the "platform" thread.
blink::TaskRunners task_runners(
thread_label_, // Dart thread labels
fsl::MessageLoop::GetCurrent()->task_runner(), // platform
host_threads_[0].TaskRunner(), // gpu
host_threads_[1].TaskRunner(), // ui
host_threads_[2].TaskRunner() // io
);
settings_.root_isolate_create_callback =
std::bind(&Engine::OnMainIsolateStart, this);
settings_.root_isolate_shutdown_callback =
std::bind([weak = weak_factory_.GetWeakPtr(),
runner = task_runners.GetPlatformTaskRunner()]() {
runner->PostTask([weak = std::move(weak)] {
if (weak) {
weak->OnMainIsolateShutdown();
}
});
});
shell_ = shell::Shell::Create(
task_runners, // host task runners
settings_, // shell launch settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
if (!shell_) {
FXL_LOG(ERROR) << "Could not launch the shell with settings: "
<< settings_.ToString();
return;
}
// Shell has been created. Before we run the engine, setup the isolate
// configurator.
{
PlatformView* platform_view =
static_cast<PlatformView*>(shell_->GetPlatformView().get());
auto& view = platform_view->GetMozartView();
component::ApplicationEnvironmentPtr application_environment;
application_context.ConnectToEnvironmentService(
application_environment.NewRequest());
isolate_configurator_ = std::make_unique<IsolateConfigurator>(
fdio_ns, //
view, //
std::move(application_environment), //
std::move(outgoing_services_request) //
);
}
// This platform does not get a separate surface platform view creation
// notification. Fire one eagerly.
shell_->GetPlatformView()->NotifyCreated();
// Launch the engine in the appropriate configuration.
auto run_configuration =
shell::RunConfiguration::InferFromSettings(settings_);
shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
fxl::MakeCopyable([engine = shell_->GetEngine(), //
run_configuration = std::move(run_configuration) //
]() mutable {
if (!engine || !engine->Run(std::move(run_configuration))) {
FXL_LOG(ERROR) << "Could not (re)launch the engine in configuration";
}
}));
UpdateNativeThreadLabelNames();
}
Engine::~Engine() {
for (const auto& thread : host_threads_) {
thread.TaskRunner()->PostTask(
[]() { fsl::MessageLoop::GetCurrent()->PostQuitTask(); });
}
}
void Engine::UpdateNativeThreadLabelNames() const {
auto set_thread_name = [](fxl::RefPtr<fxl::TaskRunner> runner,
std::string prefix, std::string suffix) {
runner->PostTask([name = prefix + suffix]() {
zx::thread::self().set_property(ZX_PROP_NAME, name.c_str(), name.size());
});
};
auto runners = shell_->GetTaskRunners();
set_thread_name(runners.GetPlatformTaskRunner(), thread_label_, ".platform");
set_thread_name(runners.GetUITaskRunner(), thread_label_, ".ui");
set_thread_name(runners.GetGPUTaskRunner(), thread_label_, ".gpu");
set_thread_name(runners.GetIOTaskRunner(), thread_label_, ".io");
}
std::pair<bool, uint32_t> Engine::GetEngineReturnCode() const {
std::pair<bool, uint32_t> code(false, 0);
if (!shell_) {
return code;
}
fxl::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(
shell_->GetTaskRunners().GetUITaskRunner(),
[&latch, &code, engine = shell_->GetEngine()]() {
if (engine) {
code = engine->GetUIIsolateReturnCode();
}
latch.Signal();
});
latch.Wait();
return code;
}
void Engine::OnMainIsolateStart() {
if (!isolate_configurator_ ||
!isolate_configurator_->ConfigureCurrentIsolate()) {
FXL_LOG(ERROR) << "Could not configure some native embedder bindings for a "
"new root isolate.";
}
}
void Engine::OnMainIsolateShutdown() {
Terminate();
}
void Engine::Terminate() {
delegate_.OnEngineTerminate(this);
// Warning. Do not do anything after this point as the delegate may have
// collected this object.
}
void Engine::OnSessionMetricsDidChange(double device_pixel_ratio) {
if (!shell_) {
return;
}
shell_->GetTaskRunners().GetPlatformTaskRunner()->PostTask(
[platform_view = shell_->GetPlatformView(), device_pixel_ratio]() {
if (platform_view) {
reinterpret_cast<flutter::PlatformView*>(platform_view.get())
->UpdateViewportMetrics(device_pixel_ratio);
}
});
}
} // namespace flutter
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include "flutter/shell/common/shell.h"
#include "isolate_configurator.h"
#include "lib/app/cpp/application_context.h"
#include "lib/fsl/threading/thread.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/weak_ptr.h"
#include "lib/ui/views/fidl/view_manager.fidl.h"
namespace flutter {
// Represents an instance of running Flutter engine along with the threads that
// host the same.
class Engine final {
public:
class Delegate {
public:
virtual void OnEngineTerminate(const Engine* holder) = 0;
};
Engine(Delegate& delegate,
std::string thread_label,
component::ApplicationContext& application_context,
blink::Settings settings,
f1dl::InterfaceRequest<mozart::ViewOwner> view_owner,
const UniqueFDIONS& fdio_ns,
f1dl::InterfaceRequest<component::ServiceProvider>
outgoing_services_request);
~Engine();
// Returns the Dart return code for the root isolate if one is present. This
// call is thread safe and synchronous. This call must be made infrequently.
std::pair<bool, uint32_t> GetEngineReturnCode() const;
private:
Delegate& delegate_;
const std::string thread_label_;
blink::Settings settings_;
std::array<fsl::Thread, 3> host_threads_;
std::unique_ptr<IsolateConfigurator> isolate_configurator_;
std::unique_ptr<shell::Shell> shell_;
fxl::WeakPtrFactory<Engine> weak_factory_;
void OnMainIsolateStart();
void OnMainIsolateShutdown();
void Terminate();
void OnSessionMetricsDidChange(double device_pixel_ratio);
void UpdateNativeThreadLabelNames() const;
FXL_DISALLOW_COPY_AND_ASSIGN(Engine);
};
} // namespace flutter
......@@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "flutter/content_handler/fuchsia_font_manager.h"
#include "fuchsia_font_manager.h"
#include <zx/vmar.h>
......@@ -32,14 +32,14 @@ void UnmapMemory(const void* buffer, void* context) {
zx::vmar::root_self().unmap(reinterpret_cast<uintptr_t>(buffer), size);
}
sk_sp<SkData> MakeSkDataFromBuffer(mem::Buffer data) {
if (!fsl::SizedVmo::IsSizeValid(data.vmo, data.size) ||
data.size > std::numeric_limits<size_t>::max()) {
sk_sp<SkData> MakeSkDataFromVMO(const fsl::SizedVmoTransportPtr& vmo) {
if (!fsl::SizedVmo::IsSizeValid(vmo->vmo, vmo->size) ||
vmo->size > std::numeric_limits<size_t>::max()) {
return nullptr;
}
uint64_t size = data.size;
uint64_t size = vmo->size;
uintptr_t buffer = 0;
zx_status_t status = zx::vmar::root_self().map(0, data.vmo, 0, size,
zx_status_t status = zx::vmar::root_self().map(0, vmo->vmo, 0, size,
ZX_VM_FLAG_PERM_READ, &buffer);
if (status != ZX_OK)
return nullptr;
......@@ -49,7 +49,7 @@ sk_sp<SkData> MakeSkDataFromBuffer(mem::Buffer data) {
fonts::FontSlant ToFontSlant(SkFontStyle::Slant slant) {
return (slant == SkFontStyle::kItalic_Slant) ? fonts::FontSlant::ITALIC
: fonts::FontSlant::UPRIGHT;
: fonts::FontSlant::UPRIGHT;
}
} // anonymous namespace
......@@ -64,7 +64,8 @@ int FuchsiaFontManager::onCountFamilies() const {
return 0;
}
void FuchsiaFontManager::onGetFamilyName(int index, SkString* familyName) const {
void FuchsiaFontManager::onGetFamilyName(int index,
SkString* familyName) const {
FXL_DCHECK(false);
}
......@@ -87,12 +88,13 @@ SkFontStyleSet* FuchsiaFontManager::onMatchFamily(
}
SkTypeface* FuchsiaFontManager::onMatchFamilyStyle(
const char family_name[], const SkFontStyle& style) const {
fonts::FontRequest request;
request.family = family_name;
request.weight = style.weight();
request.width = style.width();
request.slant = ToFontSlant(style.slant());
const char family_name[],
const SkFontStyle& style) const {
auto request = fonts::FontRequest::New();
request->family = family_name;
request->weight = style.weight();
request->width = style.width();
request->slant = ToFontSlant(style.slant());
fonts::FontResponsePtr response;
font_provider_->GetFont(
......@@ -100,14 +102,13 @@ SkTypeface* FuchsiaFontManager::onMatchFamilyStyle(
[&response](fonts::FontResponsePtr r) { response = std::move(r); });
font_provider_.WaitForResponse();
FXL_DCHECK(response)
<< "Unable to contact the font provider. Did you run "
"Flutter in an environment that has a font manager?\n";
if (!response)
if (!response) {
FXL_DLOG(ERROR) << "Unable to contact the font provider. Did you run "
"Flutter in an environment that has a font manager?";
return nullptr;
}
sk_sp<SkData> data = MakeSkDataFromBuffer(std::move(response->data.buffer));
sk_sp<SkData> data = MakeSkDataFromVMO(response->data->vmo);
if (!data)
return nullptr;
......@@ -127,13 +128,13 @@ SkTypeface* FuchsiaFontManager::onMatchFamilyStyleCharacter(
}
SkTypeface* FuchsiaFontManager::onMatchFaceStyle(const SkTypeface*,
const SkFontStyle&) const {
const SkFontStyle&) const {
FXL_DCHECK(false);
return nullptr;
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromData(sk_sp<SkData>,
int ttcIndex) const {
int ttcIndex) const {
FXL_DCHECK(false);
return nullptr;
}
......@@ -153,7 +154,7 @@ sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromStreamArgs(
}
sk_sp<SkTypeface> FuchsiaFontManager::onMakeFromFile(const char path[],
int ttcIndex) const {
int ttcIndex) const {
FXL_DCHECK(false);
return nullptr;
}
......
......@@ -26,7 +26,7 @@
namespace txt {
class FuchsiaFontManager : public SkFontMgr {
class FuchsiaFontManager final : public SkFontMgr {
public:
FuchsiaFontManager(fonts::FontProviderPtr provider);
......
// Copyright 2018 The Fuchsia 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 "isolate_configurator.h"
#include "dart-pkg/fuchsia/sdk_ext/fuchsia.h"
#include "dart-pkg/zircon/sdk_ext/handle.h"
#include "lib/tonic/converter/dart_converter.h"
#include "lib/tonic/dart_state.h"
#include "lib/tonic/logging/dart_error.h"
#include "lib/ui/flutter/sdk_ext/src/natives.h"
#include "third_party/dart/runtime/include/dart_api.h"
namespace flutter {
IsolateConfigurator::IsolateConfigurator(
const UniqueFDIONS& fdio_ns,
mozart::ViewPtr& view,
component::ApplicationEnvironmentPtr application_environment,
f1dl::InterfaceRequest<component::ServiceProvider>
outgoing_services_request)
: fdio_ns_(fdio_ns),
view_(view),
application_environment_(std::move(application_environment)),
outgoing_services_request_(std::move(outgoing_services_request)) {}
IsolateConfigurator::~IsolateConfigurator() = default;
bool IsolateConfigurator::ConfigureCurrentIsolate() {
if (used_) {
return false;
}
used_ = true;
BindFuchsia();
BindZircon();
BindDartIO();
BindScenic();
return true;
}
// |mozart::NativesDelegate|
mozart::View* IsolateConfigurator::GetMozartView() {
return view_.get();
}
void IsolateConfigurator::BindFuchsia() {
fuchsia::dart::Initialize(application_environment_.Unbind(),
std::move(outgoing_services_request_));
}
void IsolateConfigurator::BindZircon() {
// Tell dart:zircon about the FDIO namespace configured for this instance.
Dart_Handle zircon_lib = Dart_LookupLibrary(tonic::ToDart("dart:zircon"));
DART_CHECK_VALID(zircon_lib);
Dart_Handle namespace_type =
Dart_GetType(zircon_lib, tonic::ToDart("_Namespace"), 0, nullptr);
DART_CHECK_VALID(namespace_type);
DART_CHECK_VALID(
Dart_SetField(namespace_type, //
tonic::ToDart("_namespace"), //
tonic::ToDart(reinterpret_cast<intptr_t>(fdio_ns_.get()))));
}
void IsolateConfigurator::BindDartIO() {
// Grab the dart:io lib.
Dart_Handle io_lib = Dart_LookupLibrary(tonic::ToDart("dart:io"));
DART_CHECK_VALID(io_lib);
// Disable dart:io exit()
Dart_Handle embedder_config_type =
Dart_GetType(io_lib, tonic::ToDart("_EmbedderConfig"), 0, nullptr);
DART_CHECK_VALID(embedder_config_type);
DART_CHECK_VALID(Dart_SetField(embedder_config_type,
tonic::ToDart("_mayExit"), Dart_False()));
// Tell dart:io about the FDIO namespace configured for this instance.
Dart_Handle namespace_type =
Dart_GetType(io_lib, tonic::ToDart("_Namespace"), 0, nullptr);
DART_CHECK_VALID(namespace_type);
Dart_Handle namespace_args[] = {
Dart_NewInteger(reinterpret_cast<intptr_t>(fdio_ns_.get())), //
};
DART_CHECK_VALID(namespace_args[0]);
DART_CHECK_VALID(Dart_Invoke(namespace_type, tonic::ToDart("_setupNamespace"),
1, namespace_args));
}
void IsolateConfigurator::BindScenic() {
Dart_Handle mozart_internal =
Dart_LookupLibrary(tonic::ToDart("dart:mozart.internal"));
DART_CHECK_VALID(mozart_internal);
DART_CHECK_VALID(Dart_SetNativeResolver(mozart_internal, //
mozart::NativeLookup, //
mozart::NativeSymbol) //
);
DART_CHECK_VALID(Dart_SetField(
mozart_internal, //
tonic::ToDart("_context"), //
tonic::DartConverter<uint64_t>::ToDart(reinterpret_cast<intptr_t>(
static_cast<mozart::NativesDelegate*>(this)))));
mozart::ViewContainerPtr view_container;
view_->GetContainer(view_container.NewRequest());
DART_CHECK_VALID(
Dart_SetField(mozart_internal, //
tonic::ToDart("_viewContainer"), //
tonic::ToDart(zircon::dart::Handle::Create(
view_container.Unbind().TakeChannel().release()))));
}
} // namespace flutter
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include "lib/app/fidl/application_environment.fidl.h"
#include "lib/fxl/macros.h"
#include "lib/ui/flutter/sdk_ext/src/natives.h"
#include "lib/ui/views/fidl/view_containers.fidl.h"
#include "lib/ui/views/fidl/views.fidl.h"
#include "unique_fdio_ns.h"
namespace flutter {
// Contains all the information necessary to configure a new root isolate. This
// is a single use item. The lifetime of this object must extend past that of
// the root isolate.
class IsolateConfigurator final : mozart::NativesDelegate {
public:
IsolateConfigurator(
const UniqueFDIONS& fdio_ns,
mozart::ViewPtr& view,
component::ApplicationEnvironmentPtr application_environment,
f1dl::InterfaceRequest<component::ServiceProvider>
outgoing_services_request);
~IsolateConfigurator();
// Can be used only once and only on the UI thread with the newly created
// isolate already current.
bool ConfigureCurrentIsolate();
private:
bool used_ = false;
const UniqueFDIONS& fdio_ns_;
mozart::ViewPtr& view_;
component::ApplicationEnvironmentPtr application_environment_;
f1dl::InterfaceRequest<component::ServiceProvider> outgoing_services_request_;
// |mozart::NativesDelegate|
mozart::View* GetMozartView() override;
void BindFuchsia();
void BindZircon();
void BindDartIO();
void BindScenic();
FXL_DISALLOW_COPY_AND_ASSIGN(IsolateConfigurator);
};
} // namespace flutter
// Copyright 2016 The Chromium Authors. All rights reserved.
// Copyright 2018 The Fuchsia 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 <trace-provider/provider.h>
#include <cstdlib>
#include "flutter/content_handler/app.h"
#include "application_runner.h"
#include "lib/fsl/tasks/message_loop.h"
int main(int argc, const char** argv) {
int main(int argc, char const* argv[]) {
fsl::MessageLoop loop;
trace::TraceProvider provider(loop.async());
flutter_runner::App app;
FXL_DCHECK(provider.is_valid()) << "Trace provider must be valid.";
FXL_LOG(INFO) << "Flutter application services initialized.";
flutter::ApplicationRunner runner([&loop]() {
loop.PostQuitTask();
FXL_LOG(INFO) << "Flutter application services terminated. Good bye...";
});
loop.Run();
return 0;
return EXIT_SUCCESS;
}
此差异已折叠。
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <map>
#include <set>
#include "accessibility_bridge.h"
#include "flutter/lib/ui/window/viewport_metrics.h"
#include "flutter/shell/common/platform_view.h"
#include "lib/clipboard/fidl/clipboard.fidl.h"
#include "lib/fidl/cpp/bindings/binding.h"
#include "lib/fxl/macros.h"
#include "lib/ui/input/fidl/input_connection.fidl.h"
#include "lib/ui/views/fidl/view_manager.fidl.h"
#include "lib/ui/views/fidl/views.fidl.h"
#include "surface.h"
namespace flutter {
// The per engine component residing on the platform thread is responsible for
// all platform specific integrations.
class PlatformView final : public shell::PlatformView,
public mozart::ViewListener,
public mozart::InputMethodEditorClient,
public mozart::InputListener {
public:
PlatformView(
PlatformView::Delegate& delegate,
std::string debug_label,
blink::TaskRunners task_runners,
component::ServiceProviderPtr parent_environment_service_provider,
mozart::ViewManagerPtr& view_manager,
f1dl::InterfaceRequest<mozart::ViewOwner> view_owner,
ui::ScenicPtr scenic,
zx::eventpair export_token,
zx::eventpair import_token,
maxwell::ContextWriterPtr accessibility_context_writer,
OnMetricsUpdate on_session_metrics_did_change,
fxl::Closure session_error_callback);
~PlatformView();
void UpdateViewportMetrics(double pixel_ratio);
mozart::ViewPtr& GetMozartView();
private:
const std::string debug_label_;
mozart::ViewPtr view_;
f1dl::Binding<mozart::ViewListener> view_listener_;
mozart::InputConnectionPtr input_connection_;
f1dl::Binding<mozart::InputListener> input_listener_;
int current_text_input_client_ = 0;
f1dl::Binding<mozart::InputMethodEditorClient> ime_client_;
mozart::InputMethodEditorPtr ime_;
modular::ClipboardPtr clipboard_;
ui::ScenicPtr scenic_;
AccessibilityBridge accessibility_bridge_;
std::unique_ptr<Surface> surface_;
blink::LogicalMetrics metrics_;
std::set<int> down_pointers_;
std::map<
std::string /* channel */,
std::function<void(
fxl::RefPtr<blink::PlatformMessage> /* message */)> /* handler */>
platform_message_handlers_;
void RegisterPlatformMessageHandlers();
void UpdateViewportMetrics(const mozart::ViewLayoutPtr& layout);
void FlushViewportMetrics();
// |mozart::ViewListener|
void OnPropertiesChanged(
mozart::ViewPropertiesPtr properties,
const OnPropertiesChangedCallback& callback) override;
// |mozart::InputMethodEditorClient|
void DidUpdateState(mozart::TextInputStatePtr state,
mozart::InputEventPtr event) override;
// |mozart::InputMethodEditorClient|
void OnAction(mozart::InputMethodAction action) override;
// |mozart::InputListener|
void OnEvent(mozart::InputEventPtr event,
const OnEventCallback& callback) override;
bool OnHandlePointerEvent(const mozart::PointerEventPtr& pointer);
bool OnHandleKeyboardEvent(const mozart::KeyboardEventPtr& keyboard);
bool OnHandleFocusEvent(const mozart::FocusEventPtr& focus);
// |shell::PlatformView|
std::unique_ptr<shell::Surface> CreateRenderingSurface() override;
// |shell::PlatformView|
void HandlePlatformMessage(
fxl::RefPtr<blink::PlatformMessage> message) override;
// |shell::PlatformView|
void UpdateSemantics(blink::SemanticsNodeUpdates update) override;
// Channel handler for kFlutterPlatformChannel
void HandleFlutterPlatformChannelPlatformMessage(
fxl::RefPtr<blink::PlatformMessage> message);
// Channel handler for kTextInputChannel
void HandleFlutterTextInputChannelPlatformMessage(
fxl::RefPtr<blink::PlatformMessage> message);
FXL_DISALLOW_COPY_AND_ASSIGN(PlatformView);
};
} // namespace flutter
// Copyright 2016 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/content_handler/rasterizer.h"
#include "flutter/content_handler/vulkan_rasterizer.h"
namespace flutter_runner {
Rasterizer::~Rasterizer() = default;
std::unique_ptr<Rasterizer> Rasterizer::Create() {
auto vulkan_rasterizer = std::make_unique<VulkanRasterizer>();
FXL_CHECK(vulkan_rasterizer)
<< "The vulkan rasterizer must be correctly initialized.";
return vulkan_rasterizer;
}
} // namespace flutter_runner
// Copyright 2016 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.
#ifndef FLUTTER_CONTENT_HANDLER_RASTERIZER_H_
#define FLUTTER_CONTENT_HANDLER_RASTERIZER_H_
#include <memory>
#include <zx/eventpair.h>
#include "flutter/flow/layers/layer_tree.h"
#include "lib/fxl/functional/closure.h"
#include "lib/fxl/macros.h"
namespace flutter_runner {
class Rasterizer {
public:
virtual ~Rasterizer();
static std::unique_ptr<Rasterizer> Create();
virtual void SetScene(
fidl::InterfaceHandle<ui::Scenic> mozart,
zx::eventpair import_token,
fxl::Closure metrics_changed_callback) = 0;
virtual void Draw(std::unique_ptr<flow::LayerTree> layer_tree,
fxl::Closure callback) = 0;
};
} // namespace flutter_runner
#endif // FLUTTER_CONTENT_HANDLER_RASTERIZER_H_
此差异已折叠。
// Copyright 2016 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.
#ifndef FLUTTER_CONTENT_HANDLER_RUNTIME_HOLDER_H_
#define FLUTTER_CONTENT_HANDLER_RUNTIME_HOLDER_H_
#include <fdio/namespace.h>
#include <zx/channel.h>
#include <unordered_set>
#include "dart-pkg/fuchsia/sdk_ext/fuchsia.h"
#include "flutter/assets/asset_provider.h"
#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/assets/unzipper_provider.h"
#include "flutter/assets/zip_asset_store.h"
#include "flutter/content_handler/accessibility_bridge.h"
#include "flutter/flow/layers/layer_tree.h"
#include "flutter/lib/ui/window/viewport_metrics.h"
#include "flutter/runtime/runtime_controller.h"
#include "flutter/runtime/runtime_delegate.h"
#include "lib/app/cpp/application_context.h"
#include <fuchsia/cpp/component.h>
#include <fuchsia/cpp/component.h>
#include <fuchsia/cpp/modular.h>
#include "lib/fidl/cpp/binding.h"
#include "lib/fxl/functional/closure.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/weak_ptr.h"
#include "lib/ui/flutter/sdk_ext/src/natives.h"
#include <fuchsia/cpp/ui.h>
#include <fuchsia/cpp/ui.h>
#include <fuchsia/cpp/ui.h>
namespace flutter_runner {
class Rasterizer;
class RuntimeHolder : public blink::RuntimeDelegate,
public mozart::NativesDelegate,
public views_v1::ViewListener,
public input::InputListener,
public input::InputMethodEditorClient {
public:
RuntimeHolder();
~RuntimeHolder();
void Init(fdio_ns_t* namespc,
std::unique_ptr<component::ApplicationContext> context,
fidl::InterfaceRequest<component::ServiceProvider> outgoing_services,
std::vector<char> bundle);
void CreateView(const std::string& script_uri,
fidl::InterfaceRequest<views_v1_token::ViewOwner> view_owner_request,
fidl::InterfaceRequest<component::ServiceProvider> services);
Dart_Port GetUIIsolateMainPort();
std::string GetUIIsolateName();
int32_t return_code() { return return_code_; }
void SetMainIsolateShutdownCallback(std::function<void()> callback);
private:
// |blink::RuntimeDelegate| implementation:
std::string DefaultRouteName() override;
void ScheduleFrame(bool regenerate_layer_tree = true) override;
void Render(std::unique_ptr<flow::LayerTree> layer_tree) override;
void UpdateSemantics(blink::SemanticsNodeUpdates update) override;
void HandlePlatformMessage(
fxl::RefPtr<blink::PlatformMessage> message) override;
void DidCreateMainIsolate(Dart_Isolate isolate) override;
void DidShutdownMainIsolate() override;
// |mozart::NativesDelegate| implementation:
views_v1::View* GetMozartView() override;
// |input::InputListener| implementation:
void OnEvent(input::InputEvent event,
OnEventCallback callback) override;
// |views_v1::ViewListener| implementation:
void OnPropertiesChanged(
views_v1::ViewProperties properties,
OnPropertiesChangedCallback callback) override;
// |input::InputMethodEditorClient| implementation:
void DidUpdateState(input::TextInputState state,
input::InputEventPtr event) override;
void OnAction(input::InputMethodAction action) override;
fxl::WeakPtr<RuntimeHolder> GetWeakPtr();
void InitRootBundle(std::vector<char> bundle);
blink::UnzipperProvider GetUnzipperProviderForRootBundle();
bool HandleAssetPlatformMessage(blink::PlatformMessage* message);
bool GetAssetAsBuffer(const std::string& name, std::vector<uint8_t>* data);
bool HandleTextInputPlatformMessage(blink::PlatformMessage* message);
bool HandleFlutterPlatformMessage(blink::PlatformMessage* message);
void InitDartIoInternal();
void InitFuchsia();
void InitZircon();
void InitScenicInternal();
void PostBeginFrame();
void BeginFrame();
void OnFrameComplete();
void OnRedrawFrame();
void Invalidate();
fdio_ns_t* namespc_;
int dirfd_;
std::unique_ptr<component::ApplicationContext> context_;
fidl::InterfaceRequest<component::ServiceProvider> outgoing_services_;
std::vector<char> root_bundle_data_;
// TODO(zarah): Remove asset_store_ when flx is completely removed
fxl::RefPtr<blink::ZipAssetStore> asset_store_;
fxl::RefPtr<blink::AssetProvider> asset_provider_;
void* dylib_handle_ = nullptr;
std::unique_ptr<Rasterizer> rasterizer_;
std::unique_ptr<blink::RuntimeController> runtime_;
blink::ViewportMetrics viewport_metrics_;
views_v1::ViewManagerPtr view_manager_;
fidl::Binding<views_v1::ViewListener> view_listener_binding_;
fidl::Binding<input::InputListener> input_listener_binding_;
input::InputConnectionPtr input_connection_;
views_v1::ViewPtr view_;
std::unordered_set<int> down_pointers_;
input::InputMethodEditorPtr input_method_editor_;
fidl::Binding<input::InputMethodEditorClient> text_input_binding_;
int current_text_input_client_ = 0;
fxl::TimePoint last_begin_frame_time_;
bool frame_outstanding_ = false;
bool frame_scheduled_ = false;
bool frame_rendering_ = false;
int32_t return_code_ = 0;
fxl::WeakPtrFactory<RuntimeHolder> weak_factory_;
std::unique_ptr<AccessibilityBridge> accessibility_bridge_;
std::function<void()> main_isolate_shutdown_callback_;
modular::ClipboardPtr clipboard_;
FXL_DISALLOW_COPY_AND_ASSIGN(RuntimeHolder);
};
} // namespace flutter_runner
#endif // FLUTTER_CONTENT_HANDLER_RUNTIME_HOLDER_H_
// Copyright 2017 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/content_handler/service_protocol_hooks.h"
#include <string.h>
#include <string>
#include <vector>
#include "flutter/common/threads.h"
#include "flutter/content_handler/app.h"
#include "lib/fxl/memory/weak_ptr.h"
namespace flutter_runner {
namespace {
constexpr char kViewIdPrefx[] = "_flutterView/";
constexpr size_t kViewIdPrefxLength = sizeof(kViewIdPrefx) - 1;
static intptr_t KeyIndex(const char** param_keys,
intptr_t num_params,
const char* key) {
if (param_keys == NULL) {
return -1;
}
for (intptr_t i = 0; i < num_params; i++) {
if (strcmp(param_keys[i], key) == 0) {
return i;
}
}
return -1;
}
static const char* ValueForKey(const char** param_keys,
const char** param_values,
intptr_t num_params,
const char* key) {
intptr_t index = KeyIndex(param_keys, num_params, key);
if (index < 0) {
return NULL;
}
return param_values[index];
}
static void AppendIsolateRef(std::stringstream* stream,
int64_t main_port,
const std::string name) {
*stream << "{\"type\":\"@Isolate\",\"fixedId\":true,\"id\":\"isolates/";
*stream << main_port << "\",\"name\":\"" << name << "\",";
*stream << "\"number\":\"" << main_port << "\"}";
}
static void AppendFlutterView(std::stringstream* stream,
uintptr_t view_id,
int64_t isolate_id,
const std::string isolate_name) {
*stream << "{\"type\":\"FlutterView\", \"id\": \"" << kViewIdPrefx << "0x"
<< std::hex << view_id << std::dec << "\"";
if (isolate_id != ILLEGAL_PORT) {
// Append the isolate (if it exists).
*stream << ","
<< "\"isolate\":";
AppendIsolateRef(stream, isolate_id, isolate_name);
}
*stream << "}";
}
} // namespace
void ServiceProtocolHooks::RegisterHooks(bool running_precompiled_code) {
// Listing of FlutterViews.
Dart_RegisterRootServiceRequestCallback(kListViewsExtensionName, &ListViews,
nullptr);
Dart_RegisterRootServiceRequestCallback(kSetAssetBundlePathExtensionName,
&SetAssetBundlePath, nullptr);
}
const char* ServiceProtocolHooks::kListViewsExtensionName =
"_flutter.listViews";
bool ServiceProtocolHooks::ListViews(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** json_object) {
// Ask the App for the list of platform views. This will run a task on
// the UI thread before returning.
App& app = App::Shared();
std::vector<App::PlatformViewInfo> platform_views;
app.WaitForPlatformViewIds(&platform_views);
std::stringstream response;
response << "{\"type\":\"FlutterViewList\",\"views\":[";
bool prefix_comma = false;
for (auto it = platform_views.begin(); it != platform_views.end(); it++) {
uintptr_t view_id = it->view_id;
int64_t isolate_id = it->isolate_id;
const std::string& isolate_name = it->isolate_name;
if (!view_id) {
continue;
}
if (prefix_comma) {
response << ',';
} else {
prefix_comma = true;
}
AppendFlutterView(&response, view_id, isolate_id, isolate_name);
}
response << "]}";
// Copy the response.
*json_object = strdup(response.str().c_str());
return true;
}
const char* ServiceProtocolHooks::kSetAssetBundlePathExtensionName =
"_flutter.setAssetBundlePath";
bool ServiceProtocolHooks::SetAssetBundlePath(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** json_object) {
const char* view_id_str =
ValueForKey(param_keys, param_values, num_params, "viewId");
// Ask the App for the list of platform views. This will run a task on
// the UI thread before returning.
App& app = App::Shared();
std::vector<App::PlatformViewInfo> platform_views;
app.WaitForPlatformViewIds(&platform_views);
// Convert the actual flutter view hex id into a number.
uintptr_t view_id_as_num =
std::stoull((view_id_str + kViewIdPrefxLength), nullptr, 16);
// The view existed and the isolate was created. Success.
std::stringstream response;
response << "{\"type\":\"Success\","
<< "\"view\":";
for (auto it = platform_views.begin(); it != platform_views.end(); it++) {
uintptr_t view_id = it->view_id;
int64_t isolate_id = it->isolate_id;
const std::string& isolate_name = it->isolate_name;
if (!view_id || view_id != view_id_as_num) {
continue;
}
// TODO(DX): Set up asset bundle path for the isolate.
AppendFlutterView(&response, view_id, isolate_id, isolate_name);
break;
}
response << "}";
*json_object = strdup(response.str().c_str());
return true;
}
} // namespace flutter_runner
// Copyright 2017 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.
#ifndef FLUTTER_CONTENT_HANDLER_SERVICE_PROTOCOL_HOOKS_H_
#define FLUTTER_CONTENT_HANDLER_SERVICE_PROTOCOL_HOOKS_H_
#include "lib/fxl/synchronization/waitable_event.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
namespace flutter_runner {
class ServiceProtocolHooks {
public:
static void RegisterHooks(bool running_precompiled_code);
private:
static const char* kListViewsExtensionName;
static bool ListViews(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** json_object);
static const char* kSetAssetBundlePathExtensionName;
static bool SetAssetBundlePath(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** json_object);
};
} // namespace flutter_runner
#endif // FLUTTER_CONTENT_HANDLER_SERVICE_PROTOCOL_HOOKS_H_
此差异已折叠。
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include "compositor_context.h"
#include "flutter/fml/memory/weak_ptr.h"
#include "flutter/shell/common/surface.h"
#include "lib/fxl/macros.h"
namespace flutter {
// The interface between the Flutter rasterizer and the underlying platform. May
// be constructed on any thread but will be used by the engine only on the GPU
// thread.
class Surface final : public shell::Surface {
public:
Surface(const ui::ScenicPtr& scenic,
std::string debug_label,
zx::eventpair import_token,
OnMetricsUpdate session_metrics_did_change_callback,
fxl::Closure session_error_callback);
~Surface() override;
private:
const bool valid_ = CanConnectToDisplay();
const std::string debug_label_;
std::unique_ptr<CompositorContext> compositor_context_;
// |shell::Surface|
bool IsValid() override;
// |shell::Surface|
std::unique_ptr<shell::SurfaceFrame> AcquireFrame(
const SkISize& size) override;
// |shell::Surface|
GrContext* GetContext() override;
static bool CanConnectToDisplay();
FXL_DISALLOW_COPY_AND_ASSIGN(Surface);
};
} // namespace flutter
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -80,3 +80,8 @@ std::ostream& operator<<(std::ostream& os, const flow::RasterCacheKey& k) {
;
return os;
}
std::ostream& operator<<(std::ostream& os, const SkISize& size) {
os << size.width() << ", " << size.height();
return os;
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册