diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 62c4bcd7b2..f697e11094 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -481,6 +481,7 @@ LIBRARY: engine ORIGIN: ../../../flutter/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart + ../../../LICENSE TYPE: LicenseType.bsd FILE: ../../../flutter/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart +FILE: ../../../flutter/fml/paths_unittests.cc FILE: ../../../flutter/lib/ui/isolate_name_server.dart FILE: ../../../flutter/lib/ui/painting/image_encoding.cc FILE: ../../../flutter/lib/ui/painting/image_encoding.h diff --git a/engine/src/flutter/fml/BUILD.gn b/engine/src/flutter/fml/BUILD.gn index d49558bdad..94f7dafbbd 100644 --- a/engine/src/flutter/fml/BUILD.gn +++ b/engine/src/flutter/fml/BUILD.gn @@ -166,6 +166,7 @@ executable("fml_unittests") { "memory/weak_ptr_unittest.cc", "message_loop_unittests.cc", "message_unittests.cc", + "paths_unittests.cc", "string_view_unittest.cc", "synchronization/thread_annotations_unittest.cc", "synchronization/thread_checker_unittest.cc", diff --git a/engine/src/flutter/fml/paths.cc b/engine/src/flutter/fml/paths.cc index 840e211637..563595ad14 100644 --- a/engine/src/flutter/fml/paths.cc +++ b/engine/src/flutter/fml/paths.cc @@ -29,5 +29,25 @@ std::string JoinPaths(std::initializer_list components) { return stream.str(); } +std::string SanitizeURIEscapedCharacters(const std::string& str) { + std::string result; + result.reserve(str.size()); + for (std::string::size_type i = 0; i < str.size(); ++i) { + if (str[i] == '%') { + if (i > str.size() - 3 || !isxdigit(str[i + 1]) || !isxdigit(str[i + 2])) + return ""; + const std::string hex = str.substr(i + 1, 2); + const unsigned char c = strtoul(hex.c_str(), nullptr, 16); + if (!c) + return ""; + result += c; + i += 2; + } else { + result += str[i]; + } + } + return result; +} + } // namespace paths } // namespace fml diff --git a/engine/src/flutter/fml/paths.h b/engine/src/flutter/fml/paths.h index e1b2f7dbff..cccf635786 100644 --- a/engine/src/flutter/fml/paths.h +++ b/engine/src/flutter/fml/paths.h @@ -22,6 +22,12 @@ std::string AbsolutePath(const std::string& path); // Returns the directory name component of the given path. std::string GetDirectoryName(const std::string& path); +// Decodes a URI encoded string. +std::string SanitizeURIEscapedCharacters(const std::string& str); + +// Converts a file URI to a path. +std::string FromURI(const std::string& uri); + } // namespace paths } // namespace fml diff --git a/engine/src/flutter/fml/paths_unittests.cc b/engine/src/flutter/fml/paths_unittests.cc new file mode 100644 index 0000000000..a1df4ea214 --- /dev/null +++ b/engine/src/flutter/fml/paths_unittests.cc @@ -0,0 +1,17 @@ +// Copyright 2018 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 "gtest/gtest.h" + +#include "flutter/fml/paths.h" + +TEST(Paths, SanitizeURI) { + ASSERT_EQ(fml::paths::SanitizeURIEscapedCharacters("hello"), "hello"); + ASSERT_EQ(fml::paths::SanitizeURIEscapedCharacters(""), ""); + ASSERT_EQ(fml::paths::SanitizeURIEscapedCharacters("hello%20world"), + "hello world"); + ASSERT_EQ(fml::paths::SanitizeURIEscapedCharacters( + "%5Chello%5cworld%20foo%20bar%21"), + "\\hello\\world foo bar!"); +} diff --git a/engine/src/flutter/fml/platform/posix/paths_posix.cc b/engine/src/flutter/fml/platform/posix/paths_posix.cc index 98dee43666..6b93d6f97a 100644 --- a/engine/src/flutter/fml/platform/posix/paths_posix.cc +++ b/engine/src/flutter/fml/platform/posix/paths_posix.cc @@ -14,6 +14,9 @@ namespace paths { namespace { +constexpr char kFileURLPrefix[] = "file://"; +constexpr size_t kFileURLPrefixLength = sizeof(kFileURLPrefix) - 1; + std::string GetCurrentDirectory() { char buffer[PATH_MAX]; FML_CHECK(getcwd(buffer, sizeof(buffer))); @@ -44,5 +47,13 @@ std::string GetDirectoryName(const std::string& path) { return path.substr(0, separator); } +std::string FromURI(const std::string& uri) { + if (uri.substr(0, kFileURLPrefixLength) != kFileURLPrefix) + return uri; + + std::string file_path = uri.substr(kFileURLPrefixLength); + return SanitizeURIEscapedCharacters(file_path); +} + } // namespace paths -} // namespace fml \ No newline at end of file +} // namespace fml diff --git a/engine/src/flutter/fml/platform/win/paths_win.cc b/engine/src/flutter/fml/platform/win/paths_win.cc index b64aa25fff..5f6604bb39 100644 --- a/engine/src/flutter/fml/platform/win/paths_win.cc +++ b/engine/src/flutter/fml/platform/win/paths_win.cc @@ -5,6 +5,7 @@ #include "flutter/fml/paths.h" #include +#include #include "flutter/fml/paths.h" @@ -13,6 +14,9 @@ namespace paths { namespace { +constexpr char kFileURLPrefix[] = "file:///"; +constexpr size_t kFileURLPrefixLength = sizeof(kFileURLPrefix) - 1; + size_t RootLength(const std::string& path) { if (path.size() == 0) return 0; @@ -77,5 +81,14 @@ std::string GetDirectoryName(const std::string& path) { return path.substr(0, separator); } +std::string FromURI(const std::string& uri) { + if (uri.substr(0, kFileURLPrefixLength) != kFileURLPrefix) + return uri; + + std::string file_path = uri.substr(kFileURLPrefixLength); + std::replace(file_path.begin(), file_path.end(), '/', '\\'); + return SanitizeURIEscapedCharacters(file_path); +} + } // namespace paths } // namespace fml diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 5afe07691e..59272e11c0 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -865,24 +865,28 @@ bool Shell::OnServiceProtocolRunInView( return false; } - auto main_script_file = - fml::paths::AbsolutePath(params.at("mainScript").ToString()); + std::string main_script_path = + fml::paths::FromURI(params.at("mainScript").ToString()); + std::string packages_path = + fml::paths::FromURI(params.at("packagesFile").ToString()); + std::string asset_directory_path = + fml::paths::FromURI(params.at("assetDirectory").ToString()); auto main_script_file_mapping = - std::make_unique(main_script_file, false); + std::make_unique(main_script_path, false); auto isolate_configuration = blink::DartVM::IsKernelMapping(main_script_file_mapping.get()) ? IsolateConfiguration::CreateForSnapshot( std::move(main_script_file_mapping)) - : IsolateConfiguration::CreateForSource( - main_script_file, params.at("packagesFile").ToString()); + : IsolateConfiguration::CreateForSource(main_script_path, + packages_path); RunConfiguration configuration(std::move(isolate_configuration)); - configuration.AddAssetResolver(std::make_unique( - fml::OpenFile(params.at("assetDirectory").ToString().c_str(), - fml::OpenPermission::kRead, true))); + configuration.AddAssetResolver( + std::make_unique(fml::OpenFile( + asset_directory_path.c_str(), fml::OpenPermission::kRead, true))); auto& allocator = response.GetAllocator(); response.SetObject();