blob: a07b464990e7ad2f2918a34e05e9da64910f2037 [file]
// 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();
}