| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sky/tools/sky_snapshot/loader.h" |
| |
| #include <memory> |
| |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "sky/tools/sky_snapshot/logging.h" |
| #include "sky/tools/sky_snapshot/scope.h" |
| #include "sky/tools/sky_snapshot/switches.h" |
| #include "sky/engine/tonic/parsers/packages_map.h" |
| |
| namespace { |
| |
| // Extract the scheme prefix ('package:' or 'file:' from ) |
| static std::string ExtractSchemePrefix(std::string url) { |
| if (base::StartsWithASCII(url, "package:", true)) { |
| return "package:"; |
| } else if (base::StartsWithASCII(url, "file:", true)) { |
| return "file:"; |
| } |
| return ""; |
| } |
| |
| // Extract the path from a package: or file: url. |
| static std::string ExtractPath(std::string url) { |
| if (base::StartsWithASCII(url, "package:", true)) { |
| base::ReplaceFirstSubstringAfterOffset(&url, 0, "package:", ""); |
| } else if (base::StartsWithASCII(url, "file:", true)) { |
| base::ReplaceFirstSubstringAfterOffset(&url, 0, "file:", ""); |
| } |
| return url; |
| } |
| |
| base::FilePath SimplifyPath(const base::FilePath& path) { |
| std::vector<base::FilePath::StringType> components; |
| path.GetComponents(&components); |
| auto it = components.begin(); |
| base::FilePath result(*it++); |
| for (; it != components.end(); it++) { |
| auto& component = *it; |
| if (component == base::FilePath::kCurrentDirectory) |
| continue; |
| if (component == base::FilePath::kParentDirectory) |
| result = result.DirName(); |
| else |
| result = result.Append(component); |
| } |
| return result; |
| } |
| |
| class Loader { |
| public: |
| Loader(); |
| |
| void LoadPackagesMap(const base::FilePath& packages); |
| |
| const std::set<std::string>& dependencies() const { return dependencies_; } |
| |
| Dart_Handle CanonicalizeURL(Dart_Handle library, Dart_Handle url); |
| std::string GetFilePathForURL(std::string url); |
| std::string GetFilePathForPackageURL(std::string url); |
| std::string GetFilePathForFileURL(std::string url); |
| std::string Fetch(const std::string& url); |
| Dart_Handle Import(Dart_Handle url); |
| Dart_Handle Source(Dart_Handle library, Dart_Handle url); |
| |
| void set_package_root(const base::FilePath& package_root) { |
| package_root_ = package_root; |
| } |
| |
| private: |
| std::set<std::string> dependencies_; |
| base::FilePath packages_; |
| base::FilePath package_root_; |
| std::unique_ptr<tonic::PackagesMap> packages_map_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Loader); |
| }; |
| |
| Loader::Loader() { |
| } |
| |
| void Loader::LoadPackagesMap(const base::FilePath& packages) { |
| packages_ = base::MakeAbsoluteFilePath(packages); |
| dependencies_.insert(packages_.AsUTF8Unsafe()); |
| std::string packages_source; |
| if (!base::ReadFileToString(packages_, &packages_source)) { |
| fprintf(stderr, "error: Unable to load .packages file '%s'.\n", |
| packages_.AsUTF8Unsafe().c_str()); |
| exit(1); |
| } |
| packages_map_.reset(new tonic::PackagesMap()); |
| std::string error; |
| if (!packages_map_->Parse(packages_source, &error)) { |
| fprintf(stderr, "error: Unable to parse .packages file '%s'.\n%s\n", |
| packages_.AsUTF8Unsafe().c_str(), error.c_str()); |
| exit(1); |
| } |
| } |
| |
| Dart_Handle Loader::CanonicalizeURL(Dart_Handle library, Dart_Handle url) { |
| std::string string = StringFromDart(url); |
| if (base::StartsWithASCII(string, "dart:", true)) |
| return url; |
| if (base::StartsWithASCII(string, "package:", true)) |
| return url; |
| if (base::StartsWithASCII(string, "file:", true)) { |
| base::ReplaceFirstSubstringAfterOffset(&string, 0, "file:", ""); |
| return StringToDart(string);; |
| } |
| |
| std::string library_url = StringFromDart(Dart_LibraryUrl(library)); |
| std::string prefix = ExtractSchemePrefix(library_url); |
| std::string path = ExtractPath(library_url); |
| base::FilePath base_path(path); |
| base::FilePath resolved_path = base_path.DirName().Append(string); |
| base::FilePath normalized_path = SimplifyPath(resolved_path); |
| return StringToDart(prefix + normalized_path.AsUTF8Unsafe()); |
| } |
| |
| std::string Loader::GetFilePathForURL(std::string url) { |
| if (base::StartsWithASCII(url, "package:", true)) |
| return GetFilePathForPackageURL(url); |
| if (base::StartsWithASCII(url, "file:", true)) |
| return GetFilePathForFileURL(url); |
| |
| return url; |
| } |
| |
| std::string Loader::GetFilePathForPackageURL( |
| std::string url) { |
| DCHECK(base::StartsWithASCII(url, "package:", true)); |
| base::ReplaceFirstSubstringAfterOffset(&url, 0, "package:", ""); |
| size_t slash = url.find('/'); |
| if (slash == std::string::npos) |
| return std::string(); |
| std::string package = url.substr(0, slash); |
| std::string library_path = url.substr(slash + 1); |
| std::string package_path = packages_map_->Resolve(package); |
| if (package_path.empty()) |
| return std::string(); |
| if (base::StartsWithASCII(package_path, "file://", true)) { |
| base::ReplaceFirstSubstringAfterOffset(&package_path, 0, "file://", ""); |
| return package_path + library_path; |
| } |
| auto path = packages_.DirName().Append(package_path).Append(library_path); |
| return SimplifyPath(path).AsUTF8Unsafe(); |
| } |
| |
| std::string Loader::GetFilePathForFileURL(std::string url) { |
| DCHECK(base::StartsWithASCII(url, "file://", true)); |
| base::ReplaceFirstSubstringAfterOffset(&url, 0, "file://", ""); |
| return url; |
| } |
| |
| std::string Loader::Fetch(const std::string& url) { |
| std::string url_path = GetFilePathForURL(url); |
| base::FilePath path(url_path); |
| std::string source; |
| if (!base::ReadFileToString(path, &source)) { |
| fprintf(stderr, "error: Unable to find Dart library '%s'.\n", url.c_str()); |
| exit(1); |
| } |
| dependencies_.insert(url_path); |
| return source; |
| } |
| |
| Dart_Handle Loader::Import(Dart_Handle url) { |
| Dart_Handle source = StringToDart(Fetch(StringFromDart(url))); |
| Dart_Handle result = Dart_LoadLibrary(url, source, 0, 0); |
| LogIfError(result); |
| return result; |
| } |
| |
| Dart_Handle Loader::Source(Dart_Handle library, Dart_Handle url) { |
| Dart_Handle source = StringToDart(Fetch(StringFromDart(url))); |
| Dart_Handle result = Dart_LoadSource(library, url, source, 0, 0); |
| LogIfError(result); |
| return result; |
| } |
| |
| Loader* g_loader = nullptr; |
| |
| Loader& GetLoader() { |
| if (!g_loader) { |
| base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); |
| g_loader = new Loader(); |
| if (command_line.HasSwitch(switches::kPackages)) { |
| g_loader->LoadPackagesMap( |
| command_line.GetSwitchValuePath(switches::kPackages)); |
| |
| } else if (command_line.HasSwitch(switches::kPackageRoot)) { |
| g_loader->set_package_root( |
| command_line.GetSwitchValuePath(switches::kPackageRoot)); |
| } else { |
| fprintf(stderr, "error: Need either --packages or --package-root.\n"); |
| exit(1); |
| } |
| } |
| return *g_loader; |
| } |
| |
| } // namespace |
| |
| Dart_Handle HandleLibraryTag(Dart_LibraryTag tag, |
| Dart_Handle library, |
| Dart_Handle url) { |
| CHECK(Dart_IsLibrary(library)); |
| CHECK(Dart_IsString(url)); |
| |
| if (tag == Dart_kCanonicalizeUrl) |
| return GetLoader().CanonicalizeURL(library, url); |
| |
| if (tag == Dart_kImportTag) |
| return GetLoader().Import(url); |
| |
| if (tag == Dart_kSourceTag) |
| return GetLoader().Source(library, url); |
| |
| return Dart_NewApiError("Unknown library tag."); |
| } |
| |
| void LoadScript(const std::string& url) { |
| LogIfError( |
| Dart_LoadScript(StringToDart(url), StringToDart(GetLoader().Fetch(url)), |
| 0, 0)); |
| } |
| |
| const std::set<std::string>& GetDependencies() { |
| return GetLoader().dependencies(); |
| } |