// 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 "mojo/services/catalog/catalog.h" #include "base/bind.h" #include "base/json/json_file_value_serializer.h" #include "base/strings/string_split.h" #include "base/task_runner_util.h" #include "mojo/common/url_type_converters.h" #include "mojo/services/catalog/entry.h" #include "mojo/services/catalog/store.h" #include "mojo/shell/public/cpp/names.h" #include "url/gurl.h" #include "url/url_util.h" namespace catalog { namespace { base::FilePath GetManifestPath(const base::FilePath& package_dir, const std::string& name) { // TODO(beng): think more about how this should be done for exe targets. std::string type = mojo::GetNameType(name); std::string path = mojo::GetNamePath(name); if (type == mojo::kNameType_Mojo) return package_dir.AppendASCII(path + "/manifest.json"); if (type == mojo::kNameType_Exe) return package_dir.AppendASCII(path + "_manifest.json"); return base::FilePath(); } base::FilePath GetPackagePath(const base::FilePath& package_dir, const std::string& name) { std::string type = mojo::GetNameType(name); if (type == mojo::kNameType_Mojo) { // It's still a mojo: URL, use the default mapping scheme. const std::string host = mojo::GetNamePath(name); return package_dir.AppendASCII(host + "/" + host + ".mojo"); } if (type == mojo::kNameType_Exe) { #if defined OS_WIN std::string extension = ".exe"; #else std::string extension; #endif return package_dir.AppendASCII(mojo::GetNamePath(name) + extension); } return base::FilePath(); } scoped_ptr<ReadManifestResult> ReadManifest(const base::FilePath& package_dir, const std::string& name) { base::FilePath manifest_path = GetManifestPath(package_dir, name); JSONFileValueDeserializer deserializer(manifest_path); int error = 0; std::string message; // TODO(beng): probably want to do more detailed error checking. This should // be done when figuring out if to unblock connection completion. scoped_ptr<ReadManifestResult> result(new ReadManifestResult); result->manifest_root = deserializer.Deserialize(&error, &message); result->package_dir = package_dir; return result; } } // namespace ReadManifestResult::ReadManifestResult() {} ReadManifestResult::~ReadManifestResult() {} //////////////////////////////////////////////////////////////////////////////// // Catalog, public: Catalog::Catalog(scoped_ptr<Store> store, base::TaskRunner* file_task_runner) : store_(std::move(store)), file_task_runner_(file_task_runner), weak_factory_(this) { PathService::Get(base::DIR_MODULE, &system_package_dir_); DeserializeCatalog(); } Catalog::~Catalog() {} void Catalog::BindResolver(mojom::ResolverRequest request) { resolver_bindings_.AddBinding(this, std::move(request)); } void Catalog::BindShellResolver( mojo::shell::mojom::ShellResolverRequest request) { shell_resolver_bindings_.AddBinding(this, std::move(request)); } void Catalog::BindCatalog(mojom::CatalogRequest request) { catalog_bindings_.AddBinding(this, std::move(request)); } //////////////////////////////////////////////////////////////////////////////// // Catalog, mojom::Resolver: void Catalog::ResolveResponse(mojo::URLResponsePtr response, const ResolveResponseCallback& callback) { // TODO(beng): implement. } void Catalog::ResolveInterfaces(mojo::Array<mojo::String> interfaces, const ResolveInterfacesCallback& callback) { // TODO(beng): implement. } void Catalog::ResolveMIMEType(const mojo::String& mime_type, const ResolveMIMETypeCallback& callback) { // TODO(beng): implement. } void Catalog::ResolveProtocolScheme( const mojo::String& scheme, const ResolveProtocolSchemeCallback& callback) { // TODO(beng): implement. } //////////////////////////////////////////////////////////////////////////////// // Catalog, mojo::shell::mojom::ShellResolver: void Catalog::ResolveMojoName(const mojo::String& mojo_name, const ResolveMojoNameCallback& callback) { std::string type = mojo::GetNameType(mojo_name); if (type != "mojo" && type != "exe") { scoped_ptr<Entry> entry(new Entry(mojo_name)); callback.Run(mojo::shell::mojom::ResolveResult::From(*entry)); return; } auto entry = catalog_.find(mojo_name); if (entry != catalog_.end()) { callback.Run(mojo::shell::mojom::ResolveResult::From(*entry->second)); } else { base::PostTaskAndReplyWithResult( file_task_runner_, FROM_HERE, base::Bind(&ReadManifest, system_package_dir_, mojo_name), base::Bind(&Catalog::OnReadManifest, weak_factory_.GetWeakPtr(), mojo_name, callback)); } } //////////////////////////////////////////////////////////////////////////////// // Catalog, mojom::Catalog: void Catalog::GetEntries(mojo::Array<mojo::String> names, const GetEntriesCallback& callback) { mojo::Map<mojo::String, mojom::CatalogEntryPtr> entries; std::vector<mojo::String> names_vec = names.PassStorage(); for (const std::string& name : names_vec) { if (catalog_.find(name) == catalog_.end()) continue; const Entry& entry = *catalog_[name]; mojom::CatalogEntryPtr entry_ptr(mojom::CatalogEntry::New()); entry_ptr->display_name = entry.display_name(); entries[entry.name()] = std::move(entry_ptr); } callback.Run(std::move(entries)); } //////////////////////////////////////////////////////////////////////////////// // Catalog, private: void Catalog::DeserializeCatalog() { if (!store_) return; const base::ListValue* catalog = store_->GetStore(); CHECK(catalog); // TODO(sky): make this handle aliases. for (auto it = catalog->begin(); it != catalog->end(); ++it) { const base::DictionaryValue* dictionary = nullptr; const base::Value* v = *it; CHECK(v->GetAsDictionary(&dictionary)); scoped_ptr<Entry> entry = Entry::Deserialize(*dictionary); if (entry) catalog_[entry->name()] = std::move(entry); } } void Catalog::SerializeCatalog() { scoped_ptr<base::ListValue> catalog(new base::ListValue); for (const auto& entry : catalog_) catalog->Append(entry.second->Serialize()); if (store_) store_->UpdateStore(std::move(catalog)); } // static void Catalog::OnReadManifest(base::WeakPtr<Catalog> catalog, const std::string& name, const ResolveMojoNameCallback& callback, scoped_ptr<ReadManifestResult> result) { scoped_ptr<Entry> entry(new Entry(name)); if (result->manifest_root) { const base::DictionaryValue* dictionary = nullptr; CHECK(result->manifest_root->GetAsDictionary(&dictionary)); entry = Entry::Deserialize(*dictionary); } entry->set_path(GetPackagePath(result->package_dir, name)); callback.Run(mojo::shell::mojom::ResolveResult::From(*entry)); if (catalog) catalog->AddEntryToCatalog(std::move(entry)); } void Catalog::AddEntryToCatalog(scoped_ptr<Entry> entry) { DCHECK(entry); if (catalog_.end() != catalog_.find(entry->name())) return; for (auto child : entry->applications()) AddEntryToCatalog(make_scoped_ptr(child)); catalog_[entry->name()] = std::move(entry); SerializeCatalog(); } } // namespace catalog