diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 037cc20ff2702..bfe92a6422ede 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1430,6 +1430,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v1.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v1_unittest.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v2.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v2.h +FILE: ../../../flutter/shell/platform/fuchsia/flutter/component_v2_unittest.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/file_in_namespace_buffer.cc @@ -1497,6 +1498,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/platform_view_unittest.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_delegate.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_delegate.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_delegate_unittests.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/program_metadata.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index c6bcb66cf1514..ea2de3f2c4e21 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -92,6 +92,7 @@ template("runner_sources") { "platform_view.h", "pointer_delegate.cc", "pointer_delegate.h", + "program_metadata.h", "runner.cc", "runner.h", "surface.cc", @@ -459,6 +460,7 @@ if (enable_unittests) { sources = [ "accessibility_bridge_unittest.cc", "component_v1_unittest.cc", + "component_v2_unittest.cc", "flutter_runner_fakes.h", "focus_delegate_unittests.cc", "fuchsia_intl_unittest.cc", diff --git a/shell/platform/fuchsia/flutter/component_v1.cc b/shell/platform/fuchsia/flutter/component_v1.cc index 2c6b95f4c060a..7545f36cfe499 100644 --- a/shell/platform/fuchsia/flutter/component_v1.cc +++ b/shell/platform/fuchsia/flutter/component_v1.cc @@ -44,6 +44,8 @@ namespace { constexpr char kDataKey[] = "data"; constexpr char kAssetsKey[] = "assets"; +constexpr char kOldGenHeapSizeKey[] = "old_gen_heap_size"; + constexpr char kTmpPath[] = "/tmp"; constexpr char kServiceRootPath[] = "/svc"; constexpr char kRunnerConfigPath[] = "/config/data/flutter_runner_config"; @@ -59,25 +61,35 @@ std::string DebugLabelForUrl(const std::string& url) { } // namespace -void ComponentV1::ParseProgramMetadata( - const fidl::VectorPtr& program_metadata, - std::string* data_path, - std::string* assets_path) { +ProgramMetadata ComponentV1::ParseProgramMetadata( + const fidl::VectorPtr& program_metadata) { + ProgramMetadata result; if (!program_metadata.has_value()) { - return; + return result; } + for (const auto& pg : *program_metadata) { if (pg.key.compare(kDataKey) == 0) { - *data_path = "pkg/" + pg.value; + result.data_path = "pkg/" + pg.value; } else if (pg.key.compare(kAssetsKey) == 0) { - *assets_path = "pkg/" + pg.value; + result.assets_path = "pkg/" + pg.value; + } else if (pg.key.compare(kOldGenHeapSizeKey) == 0) { + int64_t specified_old_gen_heap_size = + strtol(pg.value.c_str(), nullptr /* endptr */, 10 /* base */); + if (specified_old_gen_heap_size != 0) { + result.old_gen_heap_size = specified_old_gen_heap_size; + } else { + FML_LOG(ERROR) << "Invalid old_gen_heap_size: " << pg.value; + } } } // assets_path defaults to the same as data_path if omitted. - if (assets_path->empty()) { - *assets_path = *data_path; + if (result.assets_path.empty()) { + result.assets_path = result.data_path; } + + return result; } ActiveComponentV1 ComponentV1::Create( @@ -128,12 +140,10 @@ ComponentV1::ComponentV1( settings_.dart_entrypoint_args = arguments.value(); } - // Determine where data and assets are stored within /pkg. - std::string data_path; - std::string assets_path; - ParseProgramMetadata(startup_info.program_metadata, &data_path, &assets_path); + const ProgramMetadata metadata = + ParseProgramMetadata(startup_info.program_metadata); - if (data_path.empty()) { + if (metadata.data_path.empty()) { FML_DLOG(ERROR) << "Could not find a /pkg/data directory for " << package.resolved_url; return; @@ -172,11 +182,11 @@ ComponentV1::ComponentV1( constexpr mode_t mode = O_RDONLY | O_DIRECTORY; component_assets_directory_.reset( - openat(ns_fd.get(), assets_path.c_str(), mode)); + openat(ns_fd.get(), metadata.assets_path.c_str(), mode)); FML_DCHECK(component_assets_directory_.is_valid()); component_data_directory_.reset( - openat(ns_fd.get(), data_path.c_str(), mode)); + openat(ns_fd.get(), metadata.data_path.c_str(), mode)); FML_DCHECK(component_data_directory_.is_valid()); } @@ -380,6 +390,10 @@ ComponentV1::ComponentV1( settings_.log_tag = debug_label_ + std::string{"(flutter)"}; + if (metadata.old_gen_heap_size.has_value()) { + settings_.old_gen_heap_size = *metadata.old_gen_heap_size; + } + // No asserts in debug or release product. // No asserts in release with flutter_profile=true (non-product) // Yes asserts in non-product debug. diff --git a/shell/platform/fuchsia/flutter/component_v1.h b/shell/platform/fuchsia/flutter/component_v1.h index 4fa6cbaf4091e..f0ec5a70fa581 100644 --- a/shell/platform/fuchsia/flutter/component_v1.h +++ b/shell/platform/fuchsia/flutter/component_v1.h @@ -26,6 +26,7 @@ #include "engine.h" #include "flutter_runner_product_configuration.h" +#include "program_metadata.h" #include "unique_fdio_ns.h" namespace flutter_runner { @@ -69,10 +70,8 @@ class ComponentV1 final : public Engine::Delegate, // may be collected after. ~ComponentV1(); - static void ParseProgramMetadata( - const fidl::VectorPtr& program_metadata, - std::string* data_path, - std::string* assets_path); + static ProgramMetadata ParseProgramMetadata( + const fidl::VectorPtr& program_metadata); const std::string& GetDebugLabel() const; diff --git a/shell/platform/fuchsia/flutter/component_v1_unittest.cc b/shell/platform/fuchsia/flutter/component_v1_unittest.cc index 26bcbd4c44a69..c9abaccdc6e65 100644 --- a/shell/platform/fuchsia/flutter/component_v1_unittest.cc +++ b/shell/platform/fuchsia/flutter/component_v1_unittest.cc @@ -5,46 +5,59 @@ #include "component_v1.h" #include +#include namespace flutter_runner { namespace { TEST(ComponentV1, ParseProgramMetadata) { - std::string data_path; - std::string assets_path; - // The ProgramMetadata field may be null. We should parse this as if no // fields were specified. - ComponentV1::ParseProgramMetadata(nullptr, &data_path, &assets_path); + ProgramMetadata result = ComponentV1::ParseProgramMetadata(nullptr); - EXPECT_EQ(data_path, ""); - EXPECT_EQ(assets_path, ""); + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); // The ProgramMetadata field may be empty. Treat this the same as null. fidl::VectorPtr program_metadata(size_t{0}); + result = ComponentV1::ParseProgramMetadata(program_metadata); - ComponentV1::ParseProgramMetadata(program_metadata, &data_path, &assets_path); - - EXPECT_EQ(data_path, ""); - EXPECT_EQ(assets_path, ""); + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); - // The assets_path defaults to the "data" value if unspecified + // The assets_path defaults to the "data" value if unspecified. program_metadata = {{"data", "foobar"}}; + result = ComponentV1::ParseProgramMetadata(program_metadata); - ComponentV1::ParseProgramMetadata(program_metadata, &data_path, &assets_path); + EXPECT_EQ(result.data_path, "pkg/foobar"); + EXPECT_EQ(result.assets_path, "pkg/foobar"); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); - EXPECT_EQ(data_path, "pkg/foobar"); - EXPECT_EQ(assets_path, "pkg/foobar"); + // Invalid keys are ignored. + program_metadata = {{"not_data", "foo"}, {"data", "bar"}, {"assets", "baz"}}; + result = ComponentV1::ParseProgramMetadata(program_metadata); - data_path = ""; - assets_path = ""; + EXPECT_EQ(result.data_path, "pkg/bar"); + EXPECT_EQ(result.assets_path, "pkg/baz"); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); - program_metadata = {{"not_data", "foo"}, {"data", "bar"}, {"assets", "baz"}}; + // The old_gen_heap_size can be specified. + program_metadata = {{"old_gen_heap_size", "100"}}; + result = ComponentV1::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, 100); - ComponentV1::ParseProgramMetadata(program_metadata, &data_path, &assets_path); + // Invalid old_gen_heap_sizes should be ignored. + program_metadata = {{"old_gen_heap_size", "asdf100"}}; + result = ComponentV1::ParseProgramMetadata(program_metadata); - EXPECT_EQ(data_path, "pkg/bar"); - EXPECT_EQ(assets_path, "pkg/baz"); + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); } } // anonymous namespace diff --git a/shell/platform/fuchsia/flutter/component_v2.cc b/shell/platform/fuchsia/flutter/component_v2.cc index f5f83c9ccdc4b..dafe1d3e6390c 100644 --- a/shell/platform/fuchsia/flutter/component_v2.cc +++ b/shell/platform/fuchsia/flutter/component_v2.cc @@ -27,6 +27,7 @@ #include #include "file_in_namespace_buffer.h" +#include "flutter/fml/command_line.h" #include "flutter/fml/mapping.h" #include "flutter/fml/platform/fuchsia/task_observers.h" #include "flutter/fml/synchronization/waitable_event.h" @@ -42,8 +43,15 @@ namespace flutter_runner { namespace { +// "data" and "assets" are arguments that are specific to the Flutter runner. +// They will likely go away if we migrate to the ELF runner. constexpr char kDataKey[] = "data"; constexpr char kAssetsKey[] = "assets"; + +// "args" are how the component specifies arguments to the runner. +constexpr char kArgsKey[] = "args"; +constexpr char kOldGenHeapSizeKey[] = "old_gen_heap_size"; + constexpr char kTmpPath[] = "/tmp"; constexpr char kServiceRootPath[] = "/svc"; constexpr char kRunnerConfigPath[] = "/config/data/flutter_runner_config"; @@ -57,24 +65,53 @@ std::string DebugLabelForUrl(const std::string& url) { } } +/// Parses the |args| field from the "program" field into +/// |metadata|. +void ParseArgs(std::vector& args, ProgramMetadata* metadata) { + // fml::CommandLine expects the first argument to be the name of the program, + // so we prepend a dummy argument so we can use fml::CommandLine to parse the + // arguments for us. + std::vector command_line_args = {""}; + command_line_args.insert(command_line_args.end(), args.begin(), args.end()); + fml::CommandLine parsed_args = fml::CommandLineFromIterators( + command_line_args.begin(), command_line_args.end()); + + std::string old_gen_heap_size_option; + if (parsed_args.GetOptionValue(kOldGenHeapSizeKey, + &old_gen_heap_size_option)) { + int64_t specified_old_gen_heap_size = strtol( + old_gen_heap_size_option.c_str(), nullptr /* endptr */, 10 /* base */); + if (specified_old_gen_heap_size != 0) { + metadata->old_gen_heap_size = specified_old_gen_heap_size; + } else { + FML_LOG(ERROR) << "Invalid old_gen_heap_size: " + << old_gen_heap_size_option; + } + } +} + } // namespace -void ComponentV2::ParseProgramMetadata( - const fuchsia::data::Dictionary& program_metadata, - std::string* data_path, - std::string* assets_path) { +ProgramMetadata ComponentV2::ParseProgramMetadata( + const fuchsia::data::Dictionary& program_metadata) { + ProgramMetadata result; + for (const auto& entry : program_metadata.entries()) { if (entry.key.compare(kDataKey) == 0 && entry.value != nullptr) { - *data_path = "pkg/" + entry.value->str(); + result.data_path = "pkg/" + entry.value->str(); } else if (entry.key.compare(kAssetsKey) == 0 && entry.value != nullptr) { - *assets_path = "pkg/" + entry.value->str(); + result.assets_path = "pkg/" + entry.value->str(); + } else if (entry.key.compare(kArgsKey) == 0 && entry.value != nullptr) { + ParseArgs(entry.value->str_vec(), &result); } } // assets_path defaults to the same as data_path if omitted. - if (assets_path->empty()) { - *assets_path = *data_path; + if (result.assets_path.empty()) { + result.assets_path = result.data_path; } + + return result; } ActiveComponentV2 ComponentV2::Create( @@ -124,12 +161,9 @@ ComponentV2::ComponentV2( // TODO(fxb/50694): Dart launch arguments. FML_LOG(WARNING) << "program() arguments are currently ignored (fxb/50694)."; - // Determine where data and assets are stored within /pkg. - std::string data_path; - std::string assets_path; - ParseProgramMetadata(start_info.program(), &data_path, &assets_path); + ProgramMetadata metadata = ParseProgramMetadata(start_info.program()); - if (data_path.empty()) { + if (metadata.data_path.empty()) { FML_DLOG(ERROR) << "Could not find a /pkg/data directory for " << start_info.resolved_url(); return; @@ -181,11 +215,11 @@ ComponentV2::ComponentV2( constexpr mode_t mode = O_RDONLY | O_DIRECTORY; component_assets_directory_.reset( - openat(ns_fd.get(), assets_path.c_str(), mode)); + openat(ns_fd.get(), metadata.assets_path.c_str(), mode)); FML_DCHECK(component_assets_directory_.is_valid()); component_data_directory_.reset( - openat(ns_fd.get(), data_path.c_str(), mode)); + openat(ns_fd.get(), metadata.data_path.c_str(), mode)); FML_DCHECK(component_data_directory_.is_valid()); } @@ -398,6 +432,10 @@ ComponentV2::ComponentV2( settings_.log_tag = debug_label_ + std::string{"(flutter)"}; + if (metadata.old_gen_heap_size.has_value()) { + settings_.old_gen_heap_size = *metadata.old_gen_heap_size; + } + // No asserts in debug or release product. // No asserts in release with flutter_profile=true (non-product) // Yes asserts in non-product debug. diff --git a/shell/platform/fuchsia/flutter/component_v2.h b/shell/platform/fuchsia/flutter/component_v2.h index 33acbf64e98e7..c23f4406ed7fa 100644 --- a/shell/platform/fuchsia/flutter/component_v2.h +++ b/shell/platform/fuchsia/flutter/component_v2.h @@ -27,6 +27,7 @@ #include "engine.h" #include "flutter_runner_product_configuration.h" +#include "program_metadata.h" #include "unique_fdio_ns.h" namespace flutter_runner { @@ -74,10 +75,11 @@ class ComponentV2 final // may be collected after. ~ComponentV2(); - static void ParseProgramMetadata( - const fuchsia::data::Dictionary& program_metadata, - std::string* data_path, - std::string* assets_path); + /// Parses the program metadata that was provided for the component. + /// + /// |old_gen_heap_size| will be set to -1 if no value was specified. + static ProgramMetadata ParseProgramMetadata( + const fuchsia::data::Dictionary& program_metadata); const std::string& GetDebugLabel() const; diff --git a/shell/platform/fuchsia/flutter/component_v2_unittest.cc b/shell/platform/fuchsia/flutter/component_v2_unittest.cc new file mode 100644 index 0000000000000..88cd784bf315e --- /dev/null +++ b/shell/platform/fuchsia/flutter/component_v2_unittest.cc @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "component_v2.h" + +#include +#include + +namespace flutter_runner { +namespace { + +TEST(ComponentV2, ParseProgramMetadataDefaultAssets) { + // The assets_path defaults to the "data" value if unspecified. + std::vector entries; + + fuchsia::data::DictionaryEntry data_entry; + data_entry.key = "data"; + data_entry.value = std::make_unique( + fuchsia::data::DictionaryValue::WithStr("foobar")); + entries.push_back(std::move(data_entry)); + + fuchsia::data::Dictionary program_metadata; + program_metadata.set_entries(std::move(entries)); + + ProgramMetadata result = ComponentV2::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, "pkg/foobar"); + EXPECT_EQ(result.assets_path, "pkg/foobar"); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); +} + +TEST(ComponentV2, ParseProgramMetadataIgnoreInvalidKeys) { + // Invalid keys are ignored. + std::vector entries; + + fuchsia::data::DictionaryEntry not_data_entry; + not_data_entry.key = "not_data"; + not_data_entry.value = std::make_unique( + fuchsia::data::DictionaryValue::WithStr("foo")); + entries.push_back(std::move(not_data_entry)); + + fuchsia::data::DictionaryEntry data_entry; + data_entry.key = "data"; + data_entry.value = std::make_unique( + fuchsia::data::DictionaryValue::WithStr("bar")); + entries.push_back(std::move(data_entry)); + + fuchsia::data::DictionaryEntry assets_entry; + assets_entry.key = "assets"; + assets_entry.value = std::make_unique( + fuchsia::data::DictionaryValue::WithStr("baz")); + entries.push_back(std::move(assets_entry)); + + fuchsia::data::Dictionary program_metadata; + program_metadata.set_entries(std::move(entries)); + + ProgramMetadata result = ComponentV2::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, "pkg/bar"); + EXPECT_EQ(result.assets_path, "pkg/baz"); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); +} + +TEST(ComponentV2, ParseProgramMetadataOldGenHeapSize) { + // The old_gen_heap_size can be specified. + std::vector entries; + + fuchsia::data::DictionaryEntry args_entry; + args_entry.key = "args"; + args_entry.value = std::make_unique( + fuchsia::data::DictionaryValue::WithStrVec({"--old_gen_heap_size=100"})); + entries.push_back(std::move(args_entry)); + + fuchsia::data::Dictionary program_metadata; + program_metadata.set_entries(std::move(entries)); + + ProgramMetadata result = ComponentV2::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, 100); +} + +TEST(ComponentV2, ParseProgramMetadataOldGenHeapSizeInvalid) { + // Invalid old_gen_heap_sizes should be ignored. + std::vector entries; + + fuchsia::data::DictionaryEntry args_entry; + args_entry.key = "args"; + args_entry.value = std::make_unique( + fuchsia::data::DictionaryValue::WithStrVec( + {"--old_gen_heap_size=asdf100"})); + entries.push_back(std::move(args_entry)); + + fuchsia::data::Dictionary program_metadata; + program_metadata.set_entries(std::move(entries)); + + ProgramMetadata result = ComponentV2::ParseProgramMetadata(program_metadata); + + EXPECT_EQ(result.data_path, ""); + EXPECT_EQ(result.assets_path, ""); + EXPECT_EQ(result.old_gen_heap_size, std::nullopt); +} + +} // anonymous namespace +} // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/program_metadata.h b/shell/platform/fuchsia/flutter/program_metadata.h new file mode 100644 index 0000000000000..f7040a2aacb80 --- /dev/null +++ b/shell/platform/fuchsia/flutter/program_metadata.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_PROGRAM_METADATA_H_ +#define FLUTTER_SHELL_PLATFORM_FUCHSIA_PROGRAM_METADATA_H_ + +#include +#include + +namespace flutter_runner { + +/// The metadata that can be passed by a Flutter component via +/// the `program` field. +struct ProgramMetadata { + /// The path where data for the Flutter component should + /// be stored. + std::string data_path = ""; + + /// The path where assets for the Flutter component should + /// be stored. + /// + /// TODO(fxb/89246): No components appear to be using this field. We + /// may be able to get rid of this. + std::string assets_path = ""; + + /// The preferred heap size for the Flutter component in megabytes. + std::optional old_gen_heap_size = std::nullopt; +}; + +} // namespace flutter_runner + +#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_PROGRAM_METADATA_H_