diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-07-14 17:41:05 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2016-08-04 12:37:36 +0000 |
commit | 399c965b6064c440ddcf4015f5f8e9d131c7a0a6 (patch) | |
tree | 6b06b60ff365abef0e13b3503d593a0df48d20e8 /chromium/services | |
parent | 7366110654eec46f21b6824f302356426f48cd74 (diff) | |
download | qtwebengine-chromium-399c965b6064c440ddcf4015f5f8e9d131c7a0a6.tar.gz |
BASELINE: Update Chromium to 52.0.2743.76 and Ninja to 1.7.1
Change-Id: I382f51b959689505a60f8b707255ecb344f7d8b4
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/services')
200 files changed, 11967 insertions, 0 deletions
diff --git a/chromium/services/DEPS b/chromium/services/DEPS new file mode 100644 index 00000000000..0322d918202 --- /dev/null +++ b/chromium/services/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+mojo", # By definition. + + "-services", # Services have to list which other services they depend on. + "+services/shell/public", # Every service talks to shell. +] diff --git a/chromium/services/README.md b/chromium/services/README.md new file mode 100644 index 00000000000..32fc82030a7 --- /dev/null +++ b/chromium/services/README.md @@ -0,0 +1,38 @@ +Chrome Platform Services +==== + +### Overview + +This directory contains Chrome Platform Services. If you think of Chrome as a +"portable OS," Chrome Platform Services can be thought of as that OS' "system +services". + +Roughly each subdirectory here corresponds to a service that: + + * is a client of `//services/shell` with its own unique Identity. + * could logically run a standalone process for security/performance isolation + benefits depending on the constraints of the host OS. + +### Service Directory Structure + +Individual services are structured like so: + + //services/foo/ <-- Implementation code, may have subdirs. + /public/ + /cpp/ <-- C++ client libraries (optional) + /interfaces/ <-- Mojom interfaces + +### Dependencies + +Code within `//services` may only depend on each other via each other's +`/public/` directories, i.e. implementation code cannot be shared directly. + +Service code should also take care to tightly limit the dependencies on static +libraries from outside of `//services`. Dependencies to large platform +layers like `//content`, `//chrome` or `//third_party/WebKit` must be avoided. + +### Physical Packaging + +Note that while it may be possible to build a discrete physical package (DSO) +for each service, products consuming these services may package them +differently, e.g. by combining them into a single package. diff --git a/chromium/services/catalog/BUILD.gn b/chromium/services/catalog/BUILD.gn new file mode 100644 index 00000000000..43a65e4d879 --- /dev/null +++ b/chromium/services/catalog/BUILD.gn @@ -0,0 +1,70 @@ +# 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. + +import("//mojo/public/mojo_application.gni") +import("//mojo/public/mojo_application_manifest.gni") +import("//testing/test.gni") + +group("catalog") { + testonly = true + deps = [ + ":lib", + ] +} + +source_set("lib") { + sources = [ + "catalog.cc", + "catalog.h", + "constants.cc", + "constants.h", + "entry.cc", + "entry.h", + "instance.cc", + "instance.h", + "manifest_provider.h", + "reader.cc", + "reader.h", + "store.cc", + "store.h", + "types.h", + ] + + deps = [ + "//base", + "//components/filesystem:lib", + "//services/catalog/public/interfaces", + "//services/shell/public/cpp", + ] + + public_deps = [ + # directory.mojom.h is #included by catalog.h + "//components/filesystem/public/interfaces", + ] + + data_deps = [ + ":manifest", + ] +} + +mojo_application_manifest("manifest") { + application_name = "catalog" + source = "manifest.json" +} + +source_set("unittests") { + testonly = true + sources = [ + "entry_unittest.cc", + ] + data = [ + "//services/catalog/data/", + ] + deps = [ + ":lib", + "//base", + "//services/shell/public/cpp", + "//testing/gtest", + ] +} diff --git a/chromium/services/catalog/DEPS b/chromium/services/catalog/DEPS new file mode 100644 index 00000000000..46b0331153e --- /dev/null +++ b/chromium/services/catalog/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+components/filesystem" +] diff --git a/chromium/services/catalog/OWNERS b/chromium/services/catalog/OWNERS new file mode 100644 index 00000000000..041852810f7 --- /dev/null +++ b/chromium/services/catalog/OWNERS @@ -0,0 +1,2 @@ +ben@chromium.org +rockot@chromium.org diff --git a/chromium/services/catalog/catalog.cc b/chromium/services/catalog/catalog.cc new file mode 100644 index 00000000000..52b7361992d --- /dev/null +++ b/chromium/services/catalog/catalog.cc @@ -0,0 +1,159 @@ +// 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 "services/catalog/catalog.h" + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/ptr_util.h" +#include "base/path_service.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "components/filesystem/directory_impl.h" +#include "components/filesystem/lock_table.h" +#include "components/filesystem/public/interfaces/types.mojom.h" +#include "services/catalog/constants.h" +#include "services/catalog/instance.h" +#include "services/catalog/reader.h" +#include "services/shell/public/cpp/connection.h" +#include "services/shell/public/cpp/shell_connection.h" + +namespace catalog { +namespace { + +bool IsPathNameValid(const std::string& name) { + if (name.empty() || name == "." || name == "..") + return false; + + for (auto c : name) { + if (!base::IsAsciiAlpha(c) && !base::IsAsciiDigit(c) && + c != '_' && c != '.') + return false; + } + return true; +} + +base::FilePath GetPathForApplicationName(const std::string& application_name) { + std::string path = application_name; + const bool is_mojo = + base::StartsWith(path, "mojo:", base::CompareCase::INSENSITIVE_ASCII); + const bool is_exe = + !is_mojo && + base::StartsWith(path, "exe:", base::CompareCase::INSENSITIVE_ASCII); + if (!is_mojo && !is_exe) + return base::FilePath(); + if (path.find('.') != std::string::npos) + return base::FilePath(); + if (is_mojo) + path.erase(path.begin(), path.begin() + 5); + else + path.erase(path.begin(), path.begin() + 4); + base::TrimString(path, "/", &path); + size_t end_of_name = path.find('/'); + if (end_of_name != std::string::npos) + path.erase(path.begin() + end_of_name, path.end()); + + if (!IsPathNameValid(path)) + return base::FilePath(); + + base::FilePath base_path; + PathService::Get(base::DIR_EXE, &base_path); + // TODO(beng): this won't handle user-specific components. + return base_path.AppendASCII(kMojoApplicationsDirName).AppendASCII(path). + AppendASCII("resources"); +} + +} // namespace + +Catalog::Catalog(base::SequencedWorkerPool* worker_pool, + std::unique_ptr<Store> store, + ManifestProvider* manifest_provider) + : Catalog(std::move(store)) { + system_reader_.reset(new Reader(worker_pool, manifest_provider)); + ScanSystemPackageDir(); +} + +Catalog::Catalog(base::SingleThreadTaskRunner* task_runner, + std::unique_ptr<Store> store, + ManifestProvider* manifest_provider) + : Catalog(std::move(store)) { + system_reader_.reset(new Reader(task_runner, manifest_provider)); + ScanSystemPackageDir(); +} + +Catalog::~Catalog() {} + +shell::mojom::ShellClientPtr Catalog::TakeShellClient() { + return std::move(shell_client_); +} + +Catalog::Catalog(std::unique_ptr<Store> store) + : store_(std::move(store)), weak_factory_(this) { + shell::mojom::ShellClientRequest request = GetProxy(&shell_client_); + shell_connection_.reset(new shell::ShellConnection(this, std::move(request))); +} + +void Catalog::ScanSystemPackageDir() { + base::FilePath system_package_dir; + PathService::Get(base::DIR_MODULE, &system_package_dir); + system_package_dir = system_package_dir.AppendASCII(kMojoApplicationsDirName); + system_reader_->Read(system_package_dir, &system_cache_, + base::Bind(&Catalog::SystemPackageDirScanned, + weak_factory_.GetWeakPtr())); +} + +bool Catalog::AcceptConnection(shell::Connection* connection) { + connection->AddInterface<mojom::Catalog>(this); + connection->AddInterface<filesystem::mojom::Directory>(this); + connection->AddInterface<shell::mojom::ShellResolver>(this); + return true; +} + +void Catalog::Create(shell::Connection* connection, + shell::mojom::ShellResolverRequest request) { + Instance* instance = + GetInstanceForUserId(connection->GetRemoteIdentity().user_id()); + instance->BindShellResolver(std::move(request)); +} + +void Catalog::Create(shell::Connection* connection, + mojom::CatalogRequest request) { + Instance* instance = + GetInstanceForUserId(connection->GetRemoteIdentity().user_id()); + instance->BindCatalog(std::move(request)); +} + +void Catalog::Create(shell::Connection* connection, + filesystem::mojom::DirectoryRequest request) { + if (!lock_table_) + lock_table_ = new filesystem::LockTable; + base::FilePath resources_path = + GetPathForApplicationName(connection->GetRemoteIdentity().name()); + new filesystem::DirectoryImpl(std::move(request), resources_path, + scoped_refptr<filesystem::SharedTempDir>(), + lock_table_); +} + +Instance* Catalog::GetInstanceForUserId(const std::string& user_id) { + auto it = instances_.find(user_id); + if (it != instances_.end()) + return it->second.get(); + + // TODO(beng): There needs to be a way to load the store from different users. + Instance* instance = new Instance(std::move(store_), system_reader_.get()); + instances_[user_id] = base::WrapUnique(instance); + if (loaded_) + instance->CacheReady(&system_cache_); + + return instance; +} + +void Catalog::SystemPackageDirScanned() { + loaded_ = true; + for (auto& instance : instances_) + instance.second->CacheReady(&system_cache_); +} + +} // namespace catalog diff --git a/chromium/services/catalog/catalog.h b/chromium/services/catalog/catalog.h new file mode 100644 index 00000000000..25cfc03b440 --- /dev/null +++ b/chromium/services/catalog/catalog.h @@ -0,0 +1,105 @@ +// 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. + +#ifndef SERVICES_CATALOG_CATALOG_H_ +#define SERVICES_CATALOG_CATALOG_H_ + +#include <map> +#include <memory> +#include <string> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "components/filesystem/public/interfaces/directory.mojom.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "services/catalog/public/interfaces/catalog.mojom.h" +#include "services/catalog/types.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" +#include "services/shell/public/interfaces/shell_resolver.mojom.h" + +namespace base { +class SequencedWorkerPool; +class SingleThreadTaskRunner; +} + +namespace filesystem { +class LockTable; +} + +namespace shell { +class ShellConnection; +} + +namespace catalog { + +class Instance; +class ManifestProvider; +class Reader; +class Store; + +// Creates and owns an instance of the catalog. Exposes a ShellClientPtr that +// can be passed to the Shell, potentially in a different process. +class Catalog : public shell::ShellClient, + public shell::InterfaceFactory<mojom::Catalog>, + public shell::InterfaceFactory<filesystem::mojom::Directory>, + public shell::InterfaceFactory<shell::mojom::ShellResolver> { + public: + // |manifest_provider| may be null. + Catalog(base::SequencedWorkerPool* worker_pool, + std::unique_ptr<Store> store, + ManifestProvider* manifest_provider); + Catalog(base::SingleThreadTaskRunner* task_runner, + std::unique_ptr<Store> store, + ManifestProvider* manifest_provider); + ~Catalog() override; + + shell::mojom::ShellClientPtr TakeShellClient(); + + private: + explicit Catalog(std::unique_ptr<Store> store); + + // Starts a scane for system packages. + void ScanSystemPackageDir(); + + // shell::ShellClient: + bool AcceptConnection(shell::Connection* connection) override; + + // shell::InterfaceFactory<shell::mojom::ShellResolver>: + void Create(shell::Connection* connection, + shell::mojom::ShellResolverRequest request) override; + + // shell::InterfaceFactory<mojom::Catalog>: + void Create(shell::Connection* connection, + mojom::CatalogRequest request) override; + + // shell::InterfaceFactory<filesystem::mojom::Directory>: + void Create(shell::Connection* connection, + filesystem::mojom::DirectoryRequest request) override; + + Instance* GetInstanceForUserId(const std::string& user_id); + + void SystemPackageDirScanned(); + + std::unique_ptr<Store> store_; + + shell::mojom::ShellClientPtr shell_client_; + std::unique_ptr<shell::ShellConnection> shell_connection_; + + std::map<std::string, std::unique_ptr<Instance>> instances_; + + std::unique_ptr<Reader> system_reader_; + EntryCache system_cache_; + bool loaded_ = false; + + scoped_refptr<filesystem::LockTable> lock_table_; + + base::WeakPtrFactory<Catalog> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Catalog); +}; + +} // namespace catalog + +#endif // SERVICES_CATALOG_CATALOG_H_ diff --git a/chromium/services/catalog/constants.cc b/chromium/services/catalog/constants.cc new file mode 100644 index 00000000000..eb102cbf892 --- /dev/null +++ b/chromium/services/catalog/constants.cc @@ -0,0 +1,11 @@ +// 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 "services/catalog/constants.h" + +namespace catalog { + +const char kMojoApplicationsDirName[] = "Mojo Applications"; + +} // namespace catalog diff --git a/chromium/services/catalog/constants.h b/chromium/services/catalog/constants.h new file mode 100644 index 00000000000..8942cc93a92 --- /dev/null +++ b/chromium/services/catalog/constants.h @@ -0,0 +1,14 @@ +// 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. + +#ifndef SERVICES_CATALOG_CONSTANTS_H_ +#define SERVICES_CATALOG_CONSTANTS_H_ + +namespace catalog { + +extern const char kMojoApplicationsDirName[]; + +} // namespace catalog + +#endif // SERVICES_CATALOG_CONSTANTS_H_ diff --git a/chromium/services/catalog/data/capabilities b/chromium/services/catalog/data/capabilities new file mode 100644 index 00000000000..54aa56aad33 --- /dev/null +++ b/chromium/services/catalog/data/capabilities @@ -0,0 +1,11 @@ +{ + "manifest_version": 1, + "name": "mojo:foo", + "display_name": "Foo", + "process-group": "bar", + "capabilities": { + "required": { + "mojo:bar": { "interfaces": [ "mojo::Bar" ] } + } + } +} diff --git a/chromium/services/catalog/data/instance b/chromium/services/catalog/data/instance new file mode 100644 index 00000000000..c000efd3866 --- /dev/null +++ b/chromium/services/catalog/data/instance @@ -0,0 +1,7 @@ +{ + "manifest_version": 1, + "name": "mojo:foo", + "display_name": "Foo", + "process-group": "bar", + "capabilities": { } +} diff --git a/chromium/services/catalog/data/malformed b/chromium/services/catalog/data/malformed new file mode 100644 index 00000000000..8d6705f7682 --- /dev/null +++ b/chromium/services/catalog/data/malformed @@ -0,0 +1,5 @@ +{ + "manifest_version": 1, + "name": "mojo:foo", + "display_name": "Foo", + "process-group": "bar", diff --git a/chromium/services/catalog/data/serialization b/chromium/services/catalog/data/serialization new file mode 100644 index 00000000000..460570289e2 --- /dev/null +++ b/chromium/services/catalog/data/serialization @@ -0,0 +1,18 @@ +{ + "manifest_version": 1, + "name": "mojo:foo", + "display_name": "Foo", + "process-group": "bar", + "capabilities": { + "provided": { + "foo": ["mojo::Bar", "mojo::Baz"], + "bar": ["mojo::Bork"] + }, + "required": { + "mojo:bar": { + "classes": ["a", "b"], + "interfaces": ["mojo::Blork", "mojo::Blark"] + } + } + } +} diff --git a/chromium/services/catalog/data/simple b/chromium/services/catalog/data/simple new file mode 100644 index 00000000000..e82c862ad75 --- /dev/null +++ b/chromium/services/catalog/data/simple @@ -0,0 +1,6 @@ +{ + "manifest_version": 1, + "name": "mojo:foo", + "display_name": "Foo", + "capabilities": { } +} diff --git a/chromium/services/catalog/data/wildcard_interfaces b/chromium/services/catalog/data/wildcard_interfaces new file mode 100644 index 00000000000..bb60dba459e --- /dev/null +++ b/chromium/services/catalog/data/wildcard_interfaces @@ -0,0 +1,12 @@ +{ + "manifest_version": 1, + "name": "mojo:foo", + "display_name": "Foo", + "capabilities": { + "required": { + "mojo:bar": { + "interfaces": [ "*" ] + } + } + } +} diff --git a/chromium/services/catalog/entry.cc b/chromium/services/catalog/entry.cc new file mode 100644 index 00000000000..9d477b13815 --- /dev/null +++ b/chromium/services/catalog/entry.cc @@ -0,0 +1,298 @@ +// 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 "services/catalog/entry.h" + +#include "base/values.h" +#include "services/catalog/store.h" +#include "services/shell/public/cpp/names.h" + +namespace catalog { +namespace { + +bool ReadStringSet(const base::ListValue& list_value, + std::set<std::string>* string_set) { + DCHECK(string_set); + for (auto i = list_value.begin(); i != list_value.end(); ++i) { + std::string value; + const base::Value* value_value = *i; + if (!value_value->GetAsString(&value)) { + LOG(ERROR) << "Entry::Deserialize: list member must be a string"; + return false; + } + string_set->insert(value); + } + return true; +} + +bool ReadStringSetFromValue(const base::Value& value, + std::set<std::string>* string_set) { + const base::ListValue* list_value = nullptr; + if (!value.GetAsList(&list_value)) { + LOG(ERROR) << "Entry::Deserialize: Value must be a list."; + return false; + } + return ReadStringSet(*list_value, string_set); +} + +bool ReadStringSetFromDictionary(const base::DictionaryValue& dictionary, + const std::string& key, + std::set<std::string>* string_set) { + const base::ListValue* list_value = nullptr; + if (dictionary.HasKey(key) && !dictionary.GetList(key, &list_value)) { + LOG(ERROR) << "Entry::Deserialize: " << key << " must be a list."; + return false; + } + if (list_value) + return ReadStringSet(*list_value, string_set); + return true; +} + +bool BuildCapabilities(const base::DictionaryValue& value, + shell::CapabilitySpec* capabilities) { + DCHECK(capabilities); + const base::DictionaryValue* provided_value = nullptr; + if (value.HasKey(Store::kCapabilities_ProvidedKey) && + !value.GetDictionary(Store::kCapabilities_ProvidedKey, + &provided_value)) { + LOG(ERROR) << "Entry::Deserialize: " << Store::kCapabilities_ProvidedKey + << " must be a dictionary."; + return false; + } + if (provided_value) { + shell::CapabilityRequest provided; + base::DictionaryValue::Iterator it(*provided_value); + for(; !it.IsAtEnd(); it.Advance()) { + shell::Interfaces interfaces; + if (!ReadStringSetFromValue(it.value(), &interfaces)) { + LOG(ERROR) << "Entry::Deserialize: Invalid interface list in provided " + << " classes dictionary"; + return false; + } + capabilities->provided[it.key()] = interfaces; + } + } + + const base::DictionaryValue* required_value = nullptr; + if (value.HasKey(Store::kCapabilities_RequiredKey) && + !value.GetDictionary(Store::kCapabilities_RequiredKey, + &required_value)) { + LOG(ERROR) << "Entry::Deserialize: " << Store::kCapabilities_RequiredKey + << " must be a dictionary."; + return false; + } + if (required_value) { + base::DictionaryValue::Iterator it(*required_value); + for (; !it.IsAtEnd(); it.Advance()) { + shell::CapabilityRequest spec; + const base::DictionaryValue* entry_value = nullptr; + if (!it.value().GetAsDictionary(&entry_value)) { + LOG(ERROR) << "Entry::Deserialize: " << Store::kCapabilities_RequiredKey + << " must be a dictionary."; + return false; + } + if (!ReadStringSetFromDictionary( + *entry_value, Store::kCapabilities_ClassesKey, &spec.classes)) { + LOG(ERROR) << "Entry::Deserialize: Invalid classes list in required " + << "capabilities dictionary."; + return false; + } + shell::Interfaces interfaces; + if (!ReadStringSetFromDictionary(*entry_value, + Store::kCapabilities_InterfacesKey, + &interfaces)) { + LOG(ERROR) << "Entry::Deserialize: Invalid interfaces list in required " + << "capabilities dictionary."; + return false; + } + if (interfaces.count("*") > 0) { + LOG(ERROR) << "Entry::Deserializer: Wildcard not valid in interfaces " + << "list."; + return false; + } + spec.interfaces = interfaces; + + capabilities->required[it.key()] = spec; + } + } + return true; +} + +} // namespace + +Entry::Entry() {} +Entry::Entry(const std::string& name) + : name_(name), qualifier_(shell::GetNamePath(name)), display_name_(name) {} +Entry::Entry(const Entry& other) = default; +Entry::~Entry() {} + +std::unique_ptr<base::DictionaryValue> Entry::Serialize() const { + std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue); + value->SetInteger(Store::kManifestVersionKey, 1); + value->SetString(Store::kNameKey, name_); + value->SetString(Store::kDisplayNameKey, display_name_); + value->SetString(Store::kQualifierKey, qualifier_); + std::unique_ptr<base::DictionaryValue> spec(new base::DictionaryValue); + + std::unique_ptr<base::DictionaryValue> provided(new base::DictionaryValue); + for (const auto& i : capabilities_.provided) { + std::unique_ptr<base::ListValue> interfaces(new base::ListValue); + for (const auto& interface_name : i.second) + interfaces->AppendString(interface_name); + provided->Set(i.first, std::move(interfaces)); + } + spec->Set(Store::kCapabilities_ProvidedKey, std::move(provided)); + + std::unique_ptr<base::DictionaryValue> required(new base::DictionaryValue); + for (const auto& i : capabilities_.required) { + std::unique_ptr<base::DictionaryValue> request(new base::DictionaryValue); + std::unique_ptr<base::ListValue> classes(new base::ListValue); + for (const auto& class_name : i.second.classes) + classes->AppendString(class_name); + request->Set(Store::kCapabilities_ClassesKey, std::move(classes)); + std::unique_ptr<base::ListValue> interfaces(new base::ListValue); + for (const auto& interface_name : i.second.interfaces) + interfaces->AppendString(interface_name); + request->Set(Store::kCapabilities_InterfacesKey, std::move(interfaces)); + required->Set(i.first, std::move(request)); + } + spec->Set(Store::kCapabilities_RequiredKey, std::move(required)); + + value->Set(Store::kCapabilitiesKey, std::move(spec)); + return value; +} + +// static +std::unique_ptr<Entry> Entry::Deserialize(const base::DictionaryValue& value) { + std::unique_ptr<Entry> entry(new Entry); + + // Manifest version. + int manifest_version = 0; + if (!value.GetInteger(Store::kManifestVersionKey, &manifest_version)) { + LOG(ERROR) << "Entry::Deserialize: " << Store::kManifestVersionKey + << " must be an integer."; + return nullptr; + } + if (manifest_version != 1) { + LOG(ERROR) << "Entry::Deserialize: Unsupported value of " + << Store::kManifestVersionKey << ":" << manifest_version; + return nullptr; + } + + // Name. + std::string name_string; + if (!value.GetString(Store::kNameKey, &name_string)) { + LOG(ERROR) << "Entry::Deserialize: dictionary has no " + << Store::kNameKey << " key"; + return nullptr; + } + if (!shell::IsValidName(name_string)) { + LOG(ERROR) << "Entry::Deserialize: " << name_string << " is not a valid " + << "Mojo name"; + return nullptr; + } + entry->set_name(name_string); + + // Process group. + if (value.HasKey(Store::kQualifierKey)) { + std::string qualifier; + if (!value.GetString(Store::kQualifierKey, &qualifier)) { + LOG(ERROR) << "Entry::Deserialize: " << Store::kQualifierKey << " must " + << "be a string."; + return nullptr; + } + entry->set_qualifier(qualifier); + } else { + entry->set_qualifier(shell::GetNamePath(name_string)); + } + + // Human-readable name. + std::string display_name; + if (!value.GetString(Store::kDisplayNameKey, &display_name)) { + LOG(ERROR) << "Entry::Deserialize: dictionary has no " + << Store::kDisplayNameKey << " key"; + return nullptr; + } + entry->set_display_name(display_name); + + // Capability spec. + const base::DictionaryValue* capabilities = nullptr; + if (!value.GetDictionary(Store::kCapabilitiesKey, &capabilities)) { + LOG(ERROR) << "Entry::Deserialize: dictionary has no " + << Store::kCapabilitiesKey << " key"; + return nullptr; + } + + shell::CapabilitySpec spec; + if (!BuildCapabilities(*capabilities, &spec)) { + LOG(ERROR) << "Entry::Deserialize: failed to build capability spec for " + << entry->name(); + return nullptr; + } + entry->set_capabilities(spec); + + if (value.HasKey(Store::kApplicationsKey)) { + const base::ListValue* applications = nullptr; + value.GetList(Store::kApplicationsKey, &applications); + for (size_t i = 0; i < applications->GetSize(); ++i) { + const base::DictionaryValue* application = nullptr; + applications->GetDictionary(i, &application); + std::unique_ptr<Entry> child = Entry::Deserialize(*application); + if (child) { + child->set_package(entry.get()); + // Caller must assume ownership of these items. + entry->applications_.insert(child.release()); + } + } + } + + return entry; +} + +bool Entry::ProvidesClass(const std::string& clazz) const { + return capabilities_.provided.find(clazz) != capabilities_.provided.end(); +} + +bool Entry::operator==(const Entry& other) const { + return other.name_ == name_ && other.qualifier_ == qualifier_ && + other.display_name_ == display_name_ && + other.capabilities_ == capabilities_; +} + +bool Entry::operator<(const Entry& other) const { + return std::tie(name_, qualifier_, display_name_, capabilities_) < + std::tie(other.name_, other.qualifier_, other.display_name_, + other.capabilities_); +} + +} // catalog + +namespace mojo { + +// static +shell::mojom::ResolveResultPtr + TypeConverter<shell::mojom::ResolveResultPtr, catalog::Entry>::Convert( + const catalog::Entry& input) { + shell::mojom::ResolveResultPtr result(shell::mojom::ResolveResult::New()); + result->name = input.name(); + const catalog::Entry& package = input.package() ? *input.package() : input; + result->resolved_name = package.name(); + result->qualifier = input.qualifier(); + result->capabilities = + shell::mojom::CapabilitySpec::From(input.capabilities()); + result->package_path = package.path(); + return result; +} + +// static +catalog::mojom::EntryPtr + TypeConverter<catalog::mojom::EntryPtr, catalog::Entry>::Convert( + const catalog::Entry& input) { + catalog::mojom::EntryPtr result(catalog::mojom::Entry::New()); + result->name = input.name(); + result->display_name = input.display_name(); + return result; +} + +} // namespace mojo diff --git a/chromium/services/catalog/entry.h b/chromium/services/catalog/entry.h new file mode 100644 index 00000000000..b7bdfff6ff0 --- /dev/null +++ b/chromium/services/catalog/entry.h @@ -0,0 +1,85 @@ +// 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. + +#ifndef SERVICES_CATALOG_ENTRY_H_ +#define SERVICES_CATALOG_ENTRY_H_ + +#include <memory> +#include <set> +#include <string> + +#include "base/files/file_path.h" +#include "services/catalog/public/interfaces/catalog.mojom.h" +#include "services/shell/public/cpp/capabilities.h" + +namespace base { +class DictionaryValue; +} + +namespace catalog { + +// Static information about an application package known to the Catalog. +class Entry { + public: + Entry(); + explicit Entry(const std::string& name); + explicit Entry(const Entry& other); + ~Entry(); + + std::unique_ptr<base::DictionaryValue> Serialize() const; + + // If the constructed Entry is a package that provides other Entrys, the + // caller must assume ownership of the tree of Entrys by enumerating + // applications(). + static std::unique_ptr<Entry> Deserialize(const base::DictionaryValue& value); + + bool ProvidesClass(const std::string& clazz) const; + + bool operator==(const Entry& other) const; + bool operator<(const Entry& other) const; + + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + const base::FilePath& path() const { return path_; } + void set_path(const base::FilePath& path) { path_ = path; } + const std::string& qualifier() const { return qualifier_; } + void set_qualifier(const std::string& qualifier) { qualifier_ = qualifier; } + const std::string& display_name() const { return display_name_; } + void set_display_name(const std::string& display_name) { + display_name_ = display_name; + } + const shell::CapabilitySpec& capabilities() const { return capabilities_; } + void set_capabilities(const shell::CapabilitySpec& capabilities) { + capabilities_ = capabilities; + } + const Entry* package() const { return package_; } + void set_package(Entry* package) { package_ = package; } + const std::set<Entry*>& applications() { return applications_; } + + private: + std::string name_; + base::FilePath path_; + std::string qualifier_; + std::string display_name_; + shell::CapabilitySpec capabilities_; + Entry* package_ = nullptr; + std::set<Entry*> applications_; +}; + +} // namespace catalog + +namespace mojo { +template <> +struct TypeConverter<shell::mojom::ResolveResultPtr, catalog::Entry> { + static shell::mojom::ResolveResultPtr Convert(const catalog::Entry& input); +}; + +template<> +struct TypeConverter<catalog::mojom::EntryPtr, catalog::Entry> { + static catalog::mojom::EntryPtr Convert(const catalog::Entry& input); +}; + +} // namespace mojo + +#endif // SERVICES_CATALOG_ENTRY_H_ diff --git a/chromium/services/catalog/entry_unittest.cc b/chromium/services/catalog/entry_unittest.cc new file mode 100644 index 00000000000..80dabf6e6a3 --- /dev/null +++ b/chromium/services/catalog/entry_unittest.cc @@ -0,0 +1,107 @@ +// 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 "services/catalog/entry.h" + +#include "base/files/file_path.h" +#include "base/json/json_file_value_serializer.h" +#include "base/macros.h" +#include "base/path_service.h" +#include "base/values.h" +#include "services/shell/public/cpp/capabilities.h" +#include "services/shell/public/cpp/names.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace catalog { + +class EntryTest : public testing::Test { + public: + EntryTest() {} + ~EntryTest() override {} + + protected: + std::unique_ptr<Entry> ReadEntry(const std::string& manifest, + std::unique_ptr<base::Value>* out_value) { + std::unique_ptr<base::Value> value = ReadManifest(manifest); + base::DictionaryValue* dictionary = nullptr; + CHECK(value->GetAsDictionary(&dictionary)); + if (out_value) + *out_value = std::move(value); + return Entry::Deserialize(*dictionary); + } + + std::unique_ptr<base::Value> ReadManifest(const std::string& manifest) { + base::FilePath manifest_path; + PathService::Get(base::DIR_SOURCE_ROOT, &manifest_path); + manifest_path = manifest_path.AppendASCII( + "services/catalog/data/" + manifest); + + 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. + return deserializer.Deserialize(&error, &message); + } + + private: + void SetUp() override {} + void TearDown() override {} + + DISALLOW_COPY_AND_ASSIGN(EntryTest); +}; + +TEST_F(EntryTest, Simple) { + std::unique_ptr<Entry> entry = ReadEntry("simple", nullptr); + EXPECT_EQ("mojo:foo", entry->name()); + EXPECT_EQ(shell::GetNamePath(entry->name()), entry->qualifier()); + EXPECT_EQ("Foo", entry->display_name()); +} + +TEST_F(EntryTest, NoWildcardInInterfaces) { + std::unique_ptr<Entry> entry = ReadEntry("wildcard_interfaces", nullptr); + EXPECT_EQ(nullptr, entry.get()); +} + +TEST_F(EntryTest, Instance) { + std::unique_ptr<Entry> entry = ReadEntry("instance", nullptr); + EXPECT_EQ("mojo:foo", entry->name()); + EXPECT_EQ("bar", entry->qualifier()); + EXPECT_EQ("Foo", entry->display_name()); +} + +TEST_F(EntryTest, Capabilities) { + std::unique_ptr<Entry> entry = ReadEntry("capabilities", nullptr); + + EXPECT_EQ("mojo:foo", entry->name()); + EXPECT_EQ("bar", entry->qualifier()); + EXPECT_EQ("Foo", entry->display_name()); + shell::CapabilitySpec spec; + shell::CapabilityRequest request; + request.interfaces.insert("mojo::Bar"); + spec.required["mojo:bar"] = request; + EXPECT_EQ(spec, entry->capabilities()); +} + +TEST_F(EntryTest, Serialization) { + std::unique_ptr<base::Value> value; + std::unique_ptr<Entry> entry = ReadEntry("serialization", &value); + + std::unique_ptr<base::DictionaryValue> serialized(entry->Serialize()); + + // We can't just compare values, since during deserialization some of the + // lists get converted to std::sets, which are sorted, so Value::Equals will + // fail. + std::unique_ptr<Entry> reconstituted = Entry::Deserialize(*serialized.get()); + EXPECT_EQ(*entry, *reconstituted); +} + +TEST_F(EntryTest, Malformed) { + std::unique_ptr<base::Value> value = ReadManifest("malformed"); + EXPECT_FALSE(value.get()); +} + + +} // namespace catalog diff --git a/chromium/services/catalog/instance.cc b/chromium/services/catalog/instance.cc new file mode 100644 index 00000000000..6673f35ab5e --- /dev/null +++ b/chromium/services/catalog/instance.cc @@ -0,0 +1,179 @@ +// 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 "services/catalog/instance.h" + +#include "base/bind.h" +#include "services/catalog/entry.h" +#include "services/catalog/manifest_provider.h" +#include "services/catalog/reader.h" +#include "services/catalog/store.h" +#include "services/shell/public/cpp/names.h" + +namespace catalog { +namespace { + +void AddEntry(const Entry& entry, mojo::Array<mojom::EntryPtr>* ary) { + mojom::EntryPtr entry_ptr(mojom::Entry::New()); + entry_ptr->name = entry.name(); + entry_ptr->display_name = entry.display_name(); + ary->push_back(std::move(entry_ptr)); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// Instance, public: + +Instance::Instance(std::unique_ptr<Store> store, Reader* system_reader) + : store_(std::move(store)), + system_reader_(system_reader), + weak_factory_(this) {} +Instance::~Instance() {} + +void Instance::BindShellResolver( + shell::mojom::ShellResolverRequest request) { + if (system_cache_) + shell_resolver_bindings_.AddBinding(this, std::move(request)); + else + pending_shell_resolver_requests_.push_back(std::move(request)); +} + +void Instance::BindCatalog(mojom::CatalogRequest request) { + if (system_cache_) + catalog_bindings_.AddBinding(this, std::move(request)); + else + pending_catalog_requests_.push_back(std::move(request)); +} + +void Instance::CacheReady(EntryCache* cache) { + system_cache_ = cache; + DeserializeCatalog(); + for (auto& request : pending_shell_resolver_requests_) + BindShellResolver(std::move(request)); + for (auto& request : pending_catalog_requests_) + BindCatalog(std::move(request)); +} + +//////////////////////////////////////////////////////////////////////////////// +// Instance, shell::mojom::ShellResolver: + +void Instance::ResolveMojoName(const mojo::String& mojo_name, + const ResolveMojoNameCallback& callback) { + DCHECK(system_cache_); + + std::string type = shell::GetNameType(mojo_name); + if (type != shell::kNameType_Mojo && type != shell::kNameType_Exe) { + std::unique_ptr<Entry> entry(new Entry(mojo_name)); + callback.Run(shell::mojom::ResolveResult::From(*entry)); + return; + } + + // TODO(beng): per-user catalogs. + auto entry = system_cache_->find(mojo_name); + if (entry != system_cache_->end()) { + callback.Run(shell::mojom::ResolveResult::From(*entry->second)); + return; + } + + // Manifests for mojo: names should always be in the catalog by this point. + //DCHECK(type == shell::kNameType_Exe); + system_reader_->CreateEntryForName( + mojo_name, system_cache_, + base::Bind(&Instance::OnReadManifest, weak_factory_.GetWeakPtr(), + mojo_name, callback)); +} + +//////////////////////////////////////////////////////////////////////////////// +// Instance, mojom::Catalog: + +void Instance::GetEntries(mojo::Array<mojo::String> names, + const GetEntriesCallback& callback) { + DCHECK(system_cache_); + + mojo::Array<mojom::EntryPtr> entries; + if (names.is_null()) { + // TODO(beng): user catalog. + for (const auto& entry : *system_cache_) + AddEntry(*entry.second, &entries); + } else { + std::vector<mojo::String> names_vec = names.PassStorage(); + for (const std::string& name : names_vec) { + Entry* entry = nullptr; + // TODO(beng): user catalog. + if (system_cache_->find(name) != system_cache_->end()) + entry = (*system_cache_)[name].get(); + else + continue; + AddEntry(*entry, &entries); + } + } + callback.Run(std::move(entries)); +} + +void Instance::GetEntriesProvidingClass( + const mojo::String& clazz, + const GetEntriesProvidingClassCallback& callback) { + mojo::Array<mojom::EntryPtr> entries; + for (const auto& entry : *system_cache_) + if (entry.second->ProvidesClass(clazz)) + entries.push_back(mojom::Entry::From(*entry.second)); + callback.Run(std::move(entries)); +} + +void Instance::GetEntriesConsumingMIMEType( + const mojo::String& mime_type, + const GetEntriesConsumingMIMETypeCallback& callback) { + // TODO(beng): implement. +} + +void Instance::GetEntriesSupportingScheme( + const mojo::String& scheme, + const GetEntriesSupportingSchemeCallback& callback) { + // TODO(beng): implement. +} + +//////////////////////////////////////////////////////////////////////////////// +// Instance, private: + +void Instance::DeserializeCatalog() { + DCHECK(system_cache_); + if (!store_) + return; + const base::ListValue* catalog = store_->GetStore(); + CHECK(catalog); + // TODO(sky): make this handle aliases. + // TODO(beng): implement this properly! + for (auto it = catalog->begin(); it != catalog->end(); ++it) { + const base::DictionaryValue* dictionary = nullptr; + const base::Value* v = *it; + CHECK(v->GetAsDictionary(&dictionary)); + std::unique_ptr<Entry> entry = Entry::Deserialize(*dictionary); + // TODO(beng): user catalog. + if (entry) + (*system_cache_)[entry->name()] = std::move(entry); + } +} + +void Instance::SerializeCatalog() { + DCHECK(system_cache_); + std::unique_ptr<base::ListValue> catalog(new base::ListValue); + // TODO(beng): user catalog. + for (const auto& entry : *system_cache_) + catalog->Append(entry.second->Serialize()); + if (store_) + store_->UpdateStore(std::move(catalog)); +} + +// static +void Instance::OnReadManifest(base::WeakPtr<Instance> instance, + const std::string& mojo_name, + const ResolveMojoNameCallback& callback, + shell::mojom::ResolveResultPtr result) { + callback.Run(std::move(result)); + if (instance) + instance->SerializeCatalog(); +} + +} // namespace catalog diff --git a/chromium/services/catalog/instance.h b/chromium/services/catalog/instance.h new file mode 100644 index 00000000000..a353c7d9f8c --- /dev/null +++ b/chromium/services/catalog/instance.h @@ -0,0 +1,93 @@ +// 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. + +#ifndef SERVICES_CATALOG_INSTANCE_H_ +#define SERVICES_CATALOG_INSTANCE_H_ + +#include "base/files/file_path.h" +#include "base/memory/weak_ptr.h" +#include "base/path_service.h" +#include "base/values.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/catalog/entry.h" +#include "services/catalog/public/interfaces/catalog.mojom.h" +#include "services/catalog/store.h" +#include "services/catalog/types.h" +#include "services/shell/public/cpp/interface_factory.h" +#include "services/shell/public/interfaces/shell_resolver.mojom.h" + +namespace catalog { + +class Reader; +class Store; + +class Instance : public shell::mojom::ShellResolver, + public mojom::Catalog { + public: + // |manifest_provider| may be null. + Instance(std::unique_ptr<Store> store, Reader* system_reader); + ~Instance() override; + + void BindShellResolver(shell::mojom::ShellResolverRequest request); + void BindCatalog(mojom::CatalogRequest request); + + // Called when |cache| has been populated by a directory scan. + void CacheReady(EntryCache* cache); + + private: + // shell::mojom::ShellResolver: + void ResolveMojoName(const mojo::String& mojo_name, + const ResolveMojoNameCallback& callback) override; + + // mojom::Catalog: + void GetEntries(mojo::Array<mojo::String> names, + const GetEntriesCallback& callback) override; + void GetEntriesProvidingClass( + const mojo::String& clazz, + const GetEntriesProvidingClassCallback& callback) override; + void GetEntriesConsumingMIMEType( + const mojo::String& mime_type, + const GetEntriesConsumingMIMETypeCallback& callback) override; + void GetEntriesSupportingScheme( + const mojo::String& scheme, + const GetEntriesSupportingSchemeCallback& callback) override; + + // Populate/serialize the cache from/to the supplied store. + void DeserializeCatalog(); + void SerializeCatalog(); + + // Receives the result of manifest parsing, may be received after the + // catalog object that issued the request is destroyed. + static void OnReadManifest(base::WeakPtr<Instance> instance, + const std::string& mojo_name, + const ResolveMojoNameCallback& callback, + shell::mojom::ResolveResultPtr result); + + // User-specific persistent storage of package manifests and other settings. + std::unique_ptr<Store> store_; + + mojo::BindingSet<shell::mojom::ShellResolver> shell_resolver_bindings_; + mojo::BindingSet<mojom::Catalog> catalog_bindings_; + + Reader* system_reader_; + + // A map of name -> Entry data structure for system-level packages (i.e. those + // that are visible to all users). + // TODO(beng): eventually add per-user applications. + EntryCache* system_cache_ = nullptr; + + // We only bind requests for these interfaces once the catalog has been + // populated. These data structures queue requests until that happens. + std::vector<shell::mojom::ShellResolverRequest> + pending_shell_resolver_requests_; + std::vector<mojom::CatalogRequest> pending_catalog_requests_; + + base::WeakPtrFactory<Instance> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Instance); +}; + +} // namespace catalog + +#endif // SERVICES_CATALOG_INSTANCE_H_ diff --git a/chromium/services/catalog/manifest.json b/chromium/services/catalog/manifest.json new file mode 100644 index 00000000000..3f89a0f359d --- /dev/null +++ b/chromium/services/catalog/manifest.json @@ -0,0 +1,16 @@ +{ + "manifest_version": 1, + "name": "mojo:catalog", + "display_name": "Application Resolver", + "capabilities": { + // Note, this section is provided for documentation only. Classes provided + // by this service must be stated in code in shell.cc as this manifest is + // not resolved until after several connections have been made. + "provided": { + "app": [ "filesystem::mojom::Directory" ] + }, + "required": { + "mojo:shell": { "classes": [ "shell:all_users", "shell:explicit_class" ] } + } + } +} diff --git a/chromium/services/catalog/manifest_provider.h b/chromium/services/catalog/manifest_provider.h new file mode 100644 index 00000000000..7a64e49d1b0 --- /dev/null +++ b/chromium/services/catalog/manifest_provider.h @@ -0,0 +1,29 @@ +// 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. + +#ifndef SERVICES_CATALOG_MANIFEST_PROVIDER_H_ +#define SERVICES_CATALOG_MANIFEST_PROVIDER_H_ + +#include <string> + +#include "base/strings/string_piece.h" + +namespace catalog { + +// An interface which can be implemented by a catalog embedder to override +// manifest fetching behavior. +class ManifestProvider { + public: + virtual ~ManifestProvider() {} + + // Retrieves the raw contents of the manifest for application named |name|. + // Returns true if |name| is known and |*manifest_contents| is populated. + // returns false otherwise. + virtual bool GetApplicationManifest(const base::StringPiece& name, + std::string* manifest_contents) = 0; +}; + +} // namespace catalog + +#endif // SERVICES_CATALOG_MANIFEST_PROVIDER_H_ diff --git a/chromium/services/catalog/public/cpp/BUILD.gn b/chromium/services/catalog/public/cpp/BUILD.gn new file mode 100644 index 00000000000..09e533f3727 --- /dev/null +++ b/chromium/services/catalog/public/cpp/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +source_set("cpp") { + sources = [ + "resource_loader.cc", + "resource_loader.h", + ] + + deps = [ + "//base", + "//components/filesystem/public/interfaces", + "//mojo/platform_handle", + "//mojo/public/cpp/bindings", + "//services/shell/public/cpp", + ] +} diff --git a/chromium/services/catalog/public/cpp/resource_loader.cc b/chromium/services/catalog/public/cpp/resource_loader.cc new file mode 100644 index 00000000000..16b1db8ffd5 --- /dev/null +++ b/chromium/services/catalog/public/cpp/resource_loader.cc @@ -0,0 +1,63 @@ +// 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 "services/catalog/public/cpp/resource_loader.h" + +#include <stddef.h> +#include <utility> + +#include "base/bind.h" +#include "base/files/file.h" +#include "components/filesystem/public/interfaces/directory.mojom.h" +#include "mojo/platform_handle/platform_handle_functions.h" +#include "services/shell/public/cpp/connector.h" +#include "services/shell/public/interfaces/interface_provider.mojom.h" + +namespace catalog { +namespace { +base::File GetFileFromHandle(mojo::ScopedHandle handle) { + CHECK(handle.is_valid()); + MojoPlatformHandle platform_handle; + CHECK(MojoExtractPlatformHandle(handle.release().value(), + &platform_handle) == MOJO_RESULT_OK); + return base::File(platform_handle); +} +} + +ResourceLoader::ResourceLoader() {} +ResourceLoader::~ResourceLoader() {} + +bool ResourceLoader::OpenFiles(filesystem::mojom::DirectoryPtr directory, + const std::set<std::string>& paths) { + mojo::Array<filesystem::mojom::FileOpenDetailsPtr> details( + mojo::Array<filesystem::mojom::FileOpenDetailsPtr>::New(paths.size())); + size_t i = 0; + for (const auto& path : paths) { + filesystem::mojom::FileOpenDetailsPtr open_details( + filesystem::mojom::FileOpenDetails::New()); + open_details->path = path; + open_details->open_flags = + filesystem::mojom::kFlagOpen | filesystem::mojom::kFlagRead; + details[i++] = std::move(open_details); + } + + mojo::Array<filesystem::mojom::FileOpenResultPtr> results( + mojo::Array<filesystem::mojom::FileOpenResultPtr>::New(paths.size())); + if (!directory->OpenFileHandles(std::move(details), &results)) + return false; + + for (const auto& result : results) { + resource_map_[result->path].reset( + new base::File(GetFileFromHandle(std::move(result->file_handle)))); + } + return true; +} + +base::File ResourceLoader::TakeFile(const std::string& path) { + std::unique_ptr<base::File> file_wrapper(std::move(resource_map_[path])); + resource_map_.erase(path); + return std::move(*file_wrapper); +} + +} // namespace catalog diff --git a/chromium/services/catalog/public/cpp/resource_loader.h b/chromium/services/catalog/public/cpp/resource_loader.h new file mode 100644 index 00000000000..738be3635f2 --- /dev/null +++ b/chromium/services/catalog/public/cpp/resource_loader.h @@ -0,0 +1,49 @@ +// 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. + +#ifndef SERVICES_CATALOG_PUBLIC_CPP_RESOURCE_LOADER_H_ +#define SERVICES_CATALOG_PUBLIC_CPP_RESOURCE_LOADER_H_ + +#include <map> +#include <memory> +#include <set> +#include <string> + +#include "base/macros.h" +#include "components/filesystem/public/interfaces/directory.mojom.h" + +namespace base { +class File; +} + +namespace catalog { + +// ResourceLoader asks the catalog (synchronously) to open the provided paths +// and return the file handles. Use TakeFile() to retrieve a base::File to use +// in client code. +class ResourceLoader { + public: + ResourceLoader(); + ~ResourceLoader(); + + // (Synchronously) opens all of the files in |paths| for reading. Use + // TakeFile() subsequently to obtain base::Files to use. Returns true if the + // sync operation completed, false if it did not. + bool OpenFiles(filesystem::mojom::DirectoryPtr directory, + const std::set<std::string>& paths); + + // Releases and returns the file wrapping the handle. + base::File TakeFile(const std::string& path); + + private: + using ResourceMap = std::map<std::string, std::unique_ptr<base::File>>; + + ResourceMap resource_map_; + + DISALLOW_COPY_AND_ASSIGN(ResourceLoader); +}; + +} // namespace + +#endif // SERVICES_CATALOG_PUBLIC_CPP_RESOURCE_LOADER_H_ diff --git a/chromium/services/catalog/public/interfaces/BUILD.gn b/chromium/services/catalog/public/interfaces/BUILD.gn new file mode 100644 index 00000000000..77a6aaf1308 --- /dev/null +++ b/chromium/services/catalog/public/interfaces/BUILD.gn @@ -0,0 +1,11 @@ +# 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { + sources = [ + "catalog.mojom", + ] +} diff --git a/chromium/services/catalog/public/interfaces/OWNERS b/chromium/services/catalog/public/interfaces/OWNERS new file mode 100644 index 00000000000..9e621796752 --- /dev/null +++ b/chromium/services/catalog/public/interfaces/OWNERS @@ -0,0 +1,13 @@ +# Changes to Mojo interfaces require a security review to avoid +# introducing new sandbox escapes. +per-file *.mojom=set noparent +per-file *.mojom=dcheng@chromium.org +per-file *.mojom=inferno@chromium.org +per-file *.mojom=jln@chromium.org +per-file *.mojom=jschuh@chromium.org +per-file *.mojom=kenrb@chromium.org +per-file *.mojom=mkwst@chromium.org +per-file *.mojom=nasko@chromium.org +per-file *.mojom=palmer@chromium.org +per-file *.mojom=tsepez@chromium.org +per-file *.mojom=wfh@chromium.org diff --git a/chromium/services/catalog/public/interfaces/catalog.mojom b/chromium/services/catalog/public/interfaces/catalog.mojom new file mode 100644 index 00000000000..beff6cbe979 --- /dev/null +++ b/chromium/services/catalog/public/interfaces/catalog.mojom @@ -0,0 +1,28 @@ +// 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. + +module catalog.mojom; + +struct Entry { + string name; + string display_name; +}; + +interface Catalog { + // Returns the catalog entries for the specified mojo names. + // If |names| is null, all available entries are returned. + GetEntries(array<string>? names) => (array<Entry> entries); + + // Returns the entry(ies) for applications that export to the caller the + // specified class. + GetEntriesProvidingClass(string clazz) => (array<Entry> entries); + + // Returns the entry(ies) for applications that can consume content of the + // specified MIME type. + GetEntriesConsumingMIMEType(string mime_type) => (array<Entry> entries); + + // Returns the entry(ies) for applications that can handle links with the + // specified scheme. + GetEntriesSupportingScheme(string protocol_scheme) => (array<Entry> entries); +}; diff --git a/chromium/services/catalog/reader.cc b/chromium/services/catalog/reader.cc new file mode 100644 index 00000000000..ad8dd748873 --- /dev/null +++ b/chromium/services/catalog/reader.cc @@ -0,0 +1,216 @@ +// 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 "services/catalog/reader.h" + +#include "base/bind.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" +#include "base/json/json_file_value_serializer.h" +#include "base/json/json_reader.h" +#include "base/memory/ptr_util.h" +#include "base/path_service.h" +#include "base/task_runner_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "services/catalog/constants.h" +#include "services/catalog/entry.h" +#include "services/catalog/manifest_provider.h" +#include "services/shell/public/cpp/names.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 = shell::GetNameType(name); + std::string path = shell::GetNamePath(name); + if (type == shell::kNameType_Mojo) { + return package_dir.AppendASCII(kMojoApplicationsDirName).AppendASCII( + path + "/manifest.json"); + } + if (type == shell::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 = shell::GetNameType(name); + if (type == shell::kNameType_Mojo) { + // It's still a mojo: URL, use the default mapping scheme. + const std::string host = shell::GetNamePath(name); + return package_dir.AppendASCII(host + "/" + host + ".mojo"); + } + if (type == shell::kNameType_Exe) { +#if defined OS_WIN + std::string extension = ".exe"; +#else + std::string extension; +#endif + return package_dir.AppendASCII(shell::GetNamePath(name) + extension); + } + return base::FilePath(); +} + +std::unique_ptr<Entry> ProcessManifest( + std::unique_ptr<base::Value> manifest_root, + const base::FilePath& package_dir) { + // Manifest was malformed or did not exist. + if (!manifest_root) + return nullptr; + + const base::DictionaryValue* dictionary = nullptr; + if (!manifest_root->GetAsDictionary(&dictionary)) + return nullptr; + + std::unique_ptr<Entry> entry = Entry::Deserialize(*dictionary); + if (!entry) + return nullptr; + entry->set_path(GetPackagePath(package_dir, entry->name())); + return entry; +} + +std::unique_ptr<Entry> CreateEntryForManifestAt( + const base::FilePath& manifest_path, + const base::FilePath& package_dir) { + 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. + return ProcessManifest(deserializer.Deserialize(&error, &message), + package_dir); +} + +void ScanDir( + const base::FilePath& package_dir, + const Reader::ReadManifestCallback& read_manifest_callback, + scoped_refptr<base::SingleThreadTaskRunner> original_thread_task_runner, + const base::Closure& read_complete_closure) { + base::FileEnumerator enumerator(package_dir, false, + base::FileEnumerator::DIRECTORIES); + while (1) { + base::FilePath path = enumerator.Next(); + if (path.empty()) + break; + base::FilePath manifest_path = path.AppendASCII("manifest.json"); + std::unique_ptr<Entry> entry = + CreateEntryForManifestAt(manifest_path, package_dir); + if (!entry) + continue; + + // Skip over subdirs that contain only manifests, they're artifacts of the + // build (e.g. for applications that are packaged into others) and are not + // valid standalone packages. + base::FilePath package_path = GetPackagePath(package_dir, entry->name()); + if (entry->name() != "mojo:shell" && entry->name() != "mojo:catalog" && + !base::PathExists(package_path)) { + continue; + } + + original_thread_task_runner->PostTask( + FROM_HERE, + base::Bind(read_manifest_callback, base::Passed(&entry))); + } + + original_thread_task_runner->PostTask(FROM_HERE, read_complete_closure); +} + +std::unique_ptr<Entry> ReadManifest(const base::FilePath& package_dir, + const std::string& mojo_name) { + std::unique_ptr<Entry> entry = CreateEntryForManifestAt( + GetManifestPath(package_dir, mojo_name), package_dir); + if (!entry) { + entry.reset(new Entry(mojo_name)); + entry->set_path(GetPackagePath( + package_dir.AppendASCII(kMojoApplicationsDirName), mojo_name)); + } + return entry; +} + +void AddEntryToCache(EntryCache* cache, std::unique_ptr<Entry> entry) { + for (auto child : entry->applications()) + AddEntryToCache(cache, base::WrapUnique(child)); + (*cache)[entry->name()] = std::move(entry); +} + +void DoNothing(shell::mojom::ResolveResultPtr) {} + +} // namespace + +// A sequenced task runner is used to guarantee requests are serviced in the +// order requested. To do otherwise means we may run callbacks in an +// unpredictable order, leading to flake. +Reader::Reader(base::SequencedWorkerPool* worker_pool, + ManifestProvider* manifest_provider) + : Reader(manifest_provider) { + file_task_runner_ = worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::GetSequenceToken(), + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); +} + +Reader::Reader(base::SingleThreadTaskRunner* task_runner, + ManifestProvider* manifest_provider) + : Reader(manifest_provider) { + file_task_runner_ = task_runner; +} + +Reader::~Reader() {} + +void Reader::Read(const base::FilePath& package_dir, + EntryCache* cache, + const base::Closure& read_complete_closure) { + file_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ScanDir, package_dir, + base::Bind(&Reader::OnReadManifest, weak_factory_.GetWeakPtr(), + cache, base::Bind(&DoNothing)), + base::ThreadTaskRunnerHandle::Get(), + read_complete_closure)); +} + +void Reader::CreateEntryForName( + const std::string& mojo_name, + EntryCache* cache, + const CreateEntryForNameCallback& entry_created_callback) { + std::string manifest_contents; + if (manifest_provider_ && + manifest_provider_->GetApplicationManifest(mojo_name, + &manifest_contents)) { + std::unique_ptr<base::Value> manifest_root = + base::JSONReader::Read(manifest_contents); + base::PostTaskAndReplyWithResult( + file_task_runner_.get(), FROM_HERE, + base::Bind(&ProcessManifest, base::Passed(&manifest_root), + system_package_dir_), + base::Bind(&Reader::OnReadManifest, weak_factory_.GetWeakPtr(), cache, + entry_created_callback)); + } else { + base::PostTaskAndReplyWithResult( + file_task_runner_.get(), FROM_HERE, + base::Bind(&ReadManifest, system_package_dir_, mojo_name), + base::Bind(&Reader::OnReadManifest, weak_factory_.GetWeakPtr(), cache, + entry_created_callback)); + } +} + +Reader::Reader(ManifestProvider* manifest_provider) + : manifest_provider_(manifest_provider), weak_factory_(this) { + PathService::Get(base::DIR_MODULE, &system_package_dir_); +} + +void Reader::OnReadManifest( + EntryCache* cache, + const CreateEntryForNameCallback& entry_created_callback, + std::unique_ptr<Entry> entry) { + shell::mojom::ResolveResultPtr result = + shell::mojom::ResolveResult::From(*entry); + AddEntryToCache(cache, std::move(entry)); + entry_created_callback.Run(std::move(result)); +} + +} // namespace catalog diff --git a/chromium/services/catalog/reader.h b/chromium/services/catalog/reader.h new file mode 100644 index 00000000000..1e6224bad69 --- /dev/null +++ b/chromium/services/catalog/reader.h @@ -0,0 +1,70 @@ +// 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. + +#ifndef SERVICES_CATALOG_READER_H_ +#define SERVICES_CATALOG_READER_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "services/catalog/types.h" +#include "services/shell/public/interfaces/shell_resolver.mojom.h" + +namespace base { +class SequencedWorkerPool; +class SingleThreadTaskRunner; +} + +namespace catalog { + +class Entry; +class ManifestProvider; + +// Responsible for loading manifests & building the Entry data structures. +class Reader { + public: + using ReadManifestCallback = base::Callback<void(std::unique_ptr<Entry>)>; + using CreateEntryForNameCallback = + base::Callback<void(shell::mojom::ResolveResultPtr)>; + + Reader(base::SequencedWorkerPool* worker_pool, + ManifestProvider* manifest_provider); + Reader(base::SingleThreadTaskRunner* task_runner, + ManifestProvider* manifest_provider); + ~Reader(); + + // Scans the contents of |package_dir|, reading all application manifests and + // populating |cache|. Runs |read_complete_closure| when done. + void Read(const base::FilePath& package_dir, + EntryCache* cache, + const base::Closure& read_complete_closure); + + // Returns an Entry for |mojo_name| via |callback|, assuming a manifest file + // in the canonical location + void CreateEntryForName( + const std::string& mojo_name, + EntryCache* cache, + const CreateEntryForNameCallback& entry_created_callback); + + private: + explicit Reader(ManifestProvider* manifest_provider); + + void OnReadManifest(EntryCache* cache, + const CreateEntryForNameCallback& entry_created_callback, + std::unique_ptr<Entry> entry); + + base::FilePath system_package_dir_; + scoped_refptr<base::TaskRunner> file_task_runner_; + ManifestProvider* const manifest_provider_; + base::WeakPtrFactory<Reader> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Reader); +}; + +} // namespace catalog + +#endif // SERVICES_CATALOG_READER_H_ diff --git a/chromium/services/catalog/store.cc b/chromium/services/catalog/store.cc new file mode 100644 index 00000000000..9d1cd452be8 --- /dev/null +++ b/chromium/services/catalog/store.cc @@ -0,0 +1,30 @@ +// 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 "services/catalog/store.h" + +namespace catalog { + +// static +const char Store::kManifestVersionKey[] = "manifest_version"; +// static +const char Store::kNameKey[] = "name"; +// static +const char Store::kQualifierKey[] = "process-group"; +// static +const char Store::kDisplayNameKey[] = "display_name"; +// static +const char Store::kCapabilitiesKey[] = "capabilities"; +// static +const char Store::kCapabilities_ProvidedKey[] = "provided"; +// static +const char Store::kCapabilities_RequiredKey[] = "required"; +// static +const char Store::kCapabilities_ClassesKey[] = "classes"; +// static +const char Store::kCapabilities_InterfacesKey[] = "interfaces"; +// static +const char Store::kApplicationsKey[] = "applications"; + +} // namespace catalog diff --git a/chromium/services/catalog/store.h b/chromium/services/catalog/store.h new file mode 100644 index 00000000000..5fe92c08513 --- /dev/null +++ b/chromium/services/catalog/store.h @@ -0,0 +1,55 @@ +// 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. + +#ifndef SERVICES_CATALOG_STORE_H_ +#define SERVICES_CATALOG_STORE_H_ + +#include <memory> + +#include "base/values.h" + +namespace catalog { + +// Implemented by an object that provides storage for the application catalog +// (e.g. in Chrome, preferences). The Catalog is the canonical owner of the +// contents of the store, so no one else must modify its contents. +class Store { + public: + // Value is an integer. + static const char kManifestVersionKey[]; + // Value is a string. + static const char kNameKey[]; + // Value is a string. + static const char kQualifierKey[]; + // Value is a string. + static const char kDisplayNameKey[]; + // Value is a dictionary. + static const char kCapabilitiesKey[]; + // Value is a dictionary. + static const char kCapabilities_ProvidedKey[]; + // Value is a dictionary. + static const char kCapabilities_RequiredKey[]; + // Value is a list. + static const char kCapabilities_ClassesKey[]; + // Value is a list. + static const char kCapabilities_InterfacesKey[]; + // Value is a list. + static const char kApplicationsKey[]; + + virtual ~Store() {} + + // Called during initialization to construct the Catalog's catalog. + // Returns a serialized list of the apps. Each entry in the returned list + // corresponds to an app (as a dictionary). Each dictionary has a name, + // display name and capabilities. The return value is owned by the caller. + virtual const base::ListValue* GetStore() = 0; + + // Write the catalog to the store. Called when the Catalog learns of a newly + // encountered application. + virtual void UpdateStore(std::unique_ptr<base::ListValue> store) = 0; +}; + +} // namespace catalog + +#endif // SERVICES_CATALOG_STORE_H_ diff --git a/chromium/services/catalog/types.h b/chromium/services/catalog/types.h new file mode 100644 index 00000000000..00d3cc58586 --- /dev/null +++ b/chromium/services/catalog/types.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef SERVICES_CATALOG_TYPES_H_ +#define SERVICES_CATALOG_TYPES_H_ + +#include <map> +#include <memory> +#include <string> + + +namespace catalog { + +class Entry; + +// A map of mojo names -> catalog |Entry|s. +using EntryCache = std::map<std::string, std::unique_ptr<Entry>>; + +} // namespace catalog + +#endif // SERVICES_CATALOG_TYPES_H_ diff --git a/chromium/services/navigation/BUILD.gn b/chromium/services/navigation/BUILD.gn new file mode 100644 index 00000000000..0a70e16e4b6 --- /dev/null +++ b/chromium/services/navigation/BUILD.gn @@ -0,0 +1,143 @@ +# 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. + +import("//mojo/public/mojo_application.gni") +import("//mojo/public/mojo_application_manifest.gni") +import("//testing/test.gni") +import("//tools/grit/repack.gni") + +group("all") { + testonly = true + data_deps = [ + ":navigation", + ":navigation_unittests", + ] +} + +executable("navigation") { + testonly = true + sources = [ + "main.cc", + ] + + deps = [ + ":lib", + ":pak", + "//base", + "//build/win:default_exe_manifest", + "//components/mus/public/cpp", + "//content", + "//content/public/app:both", + "//mojo/public/cpp/bindings", + "//services/navigation/content_client", + "//services/shell/runner:init", + "//services/shell/runner/common", + "//ui/views", + "//ui/views/controls/webview", + "//ui/views/mus", + ] + + data_deps = [ + ":manifest", + "//mash/wm", + ] + + if (is_win) { + configs -= [ "//build/config/win:console" ] + configs += [ "//build/config/win:windowed" ] + deps += [ "//sandbox" ] + } +} + +static_library("lib") { + sources = [ + "navigation.cc", + "navigation.h", + "view_impl.cc", + "view_impl.h", + ] + + deps = [ + "//base", + "//components/mus/public/cpp", + "//content/public/browser", + "//mojo/converters/geometry", + "//services/navigation/public/interfaces", + "//services/shell/public/cpp", + "//skia", + "//ui/views", + "//ui/views/controls/webview", + "//ui/views/mus", + ] +} + +test("navigation_unittests") { + sources = [ + "navigation_unittest.cc", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//services/navigation/public/interfaces", + "//services/shell/public/cpp", + "//services/shell/public/cpp:shell_test_support", + "//services/shell/public/cpp/test:run_all_shelltests", + "//testing/gtest", + ] + + data_deps = [ + ":navigation", + ":unittest_manifest", + "//components/mus/test_wm", + ] +} + +mojo_application_manifest("manifest") { + type = "exe" + application_name = "navigation" + source = "manifest.json" +} + +mojo_application_manifest("unittest_manifest") { + type = "exe" + application_name = "navigation_unittests" + source = "unittest_manifest.json" +} + +repack("pak") { + sources = [ + "$root_gen_dir/blink/devtools_resources.pak", + "$root_gen_dir/blink/public/resources/blink_image_resources_100_percent.pak", + "$root_gen_dir/blink/public/resources/blink_resources.pak", + "$root_gen_dir/content/app/resources/content_resources_100_percent.pak", + "$root_gen_dir/content/app/strings/content_strings_en-US.pak", + "$root_gen_dir/content/browser/tracing/tracing_resources.pak", + "$root_gen_dir/content/content_resources.pak", + "$root_gen_dir/content/shell/shell_resources.pak", + "$root_gen_dir/net/net_resources.pak", + "$root_gen_dir/ui/resources/ui_resources_100_percent.pak", + "$root_gen_dir/ui/resources/webui_resources.pak", + "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak", + "$root_gen_dir/ui/strings/ui_strings_en-US.pak", + "$root_gen_dir/ui/views/resources/views_resources_100_percent.pak", + ] + + deps = [ + "//content:resources", + "//content/app/resources", + "//content/app/strings", + "//content/browser/devtools:resources", + "//content/browser/tracing:resources", + "//content/shell:resources", + "//net:net_resources", + "//third_party/WebKit/public:image_resources", + "//third_party/WebKit/public:resources", + "//ui/resources", + "//ui/strings", + "//ui/views/resources", + ] + + output = "$root_out_dir/navigation.pak" +} diff --git a/chromium/services/navigation/DEPS b/chromium/services/navigation/DEPS new file mode 100644 index 00000000000..96712a72022 --- /dev/null +++ b/chromium/services/navigation/DEPS @@ -0,0 +1,9 @@ +include_rules = [ + "+components/mus/public/cpp", + "+content/public", + "+content/shell", + "+net/base", + "+sandbox", + "+services/shell", + "+ui" +] diff --git a/chromium/services/navigation/content_client/BUILD.gn b/chromium/services/navigation/content_client/BUILD.gn new file mode 100644 index 00000000000..fbf2181b58c --- /dev/null +++ b/chromium/services/navigation/content_client/BUILD.gn @@ -0,0 +1,24 @@ +# 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. + +static_library("content_client") { + testonly = true + sources = [ + "browser_main_parts.cc", + "browser_main_parts.h", + "content_browser_client.cc", + "content_browser_client.h", + "main_delegate.cc", + "main_delegate.h", + ] + + deps = [ + "//base:i18n", + "//content", + "//content/shell:content_shell_lib", + "//net", + "//services/navigation:lib", + "//ui/views:test_support", + ] +} diff --git a/chromium/services/navigation/content_client/browser_main_parts.cc b/chromium/services/navigation/content_client/browser_main_parts.cc new file mode 100644 index 00000000000..09a688b3c99 --- /dev/null +++ b/chromium/services/navigation/content_client/browser_main_parts.cc @@ -0,0 +1,43 @@ +// 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 "services/navigation/content_client/browser_main_parts.h" + +#include "base/message_loop/message_loop.h" +#include "content/public/common/mojo_shell_connection.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/shell/browser/shell_net_log.h" +#include "services/navigation/navigation.h" +#include "ui/views/test/test_views_delegate.h" + +namespace navigation { + +BrowserMainParts::BrowserMainParts( + const content::MainFunctionParams& parameters) {} +BrowserMainParts::~BrowserMainParts() {} + +void BrowserMainParts::ToolkitInitialized() { + if (!views::ViewsDelegate::GetInstance()) + views_delegate_.reset(new views::TestViewsDelegate); +} + +void BrowserMainParts::PreMainMessageLoopRun() { + net_log_.reset(new content::ShellNetLog("ash_shell")); + browser_context_.reset( + new content::ShellBrowserContext(false, net_log_.get())); + navigation_->Init(content::MojoShellConnection::Get()->GetConnector(), + browser_context()); +} + +void BrowserMainParts::PostMainMessageLoopRun() { + views_delegate_.reset(); + browser_context_.reset(); +} + +bool BrowserMainParts::MainMessageLoopRun(int* result_code) { + base::MessageLoop::current()->Run(); + return true; +} + +} // namespace navigation diff --git a/chromium/services/navigation/content_client/browser_main_parts.h b/chromium/services/navigation/content_client/browser_main_parts.h new file mode 100644 index 00000000000..a5f69adc6da --- /dev/null +++ b/chromium/services/navigation/content_client/browser_main_parts.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef SERVICES_NAVIGATION_CONTENT_CLIENT_BROWSER_MAIN_PARTS_H_ +#define SERVICES_NAVIGATION_CONTENT_CLIENT_BROWSER_MAIN_PARTS_H_ + +#include <memory> + +#include "base/macros.h" +#include "content/public/browser/browser_main_parts.h" + +namespace content { +class ShellBrowserContext; +struct MainFunctionParams; +} + +namespace net { +class NetLog; +} + +namespace views { +class ViewsDelegate; +} + +namespace navigation { + +class Navigation; + +class BrowserMainParts : public content::BrowserMainParts { + public: + BrowserMainParts(const content::MainFunctionParams& parameters); + ~BrowserMainParts() override; + + // Overridden from content::BrowserMainParts: + void ToolkitInitialized() override; + void PreMainMessageLoopRun() override; + bool MainMessageLoopRun(int* result_code) override; + void PostMainMessageLoopRun() override; + + content::ShellBrowserContext* browser_context() { + return browser_context_.get(); + } + + void set_navigation(Navigation* navigation) { navigation_ = navigation; } + + private: + std::unique_ptr<net::NetLog> net_log_; + std::unique_ptr<content::ShellBrowserContext> browser_context_; + std::unique_ptr<views::ViewsDelegate> views_delegate_; + + Navigation* navigation_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(BrowserMainParts); +}; + +} // namespace navigation + +#endif // SERVICES_NAVIGATION_CONTENT_CLIENT_BROWSER_MAIN_PARTS_H_ diff --git a/chromium/services/navigation/content_client/content_browser_client.cc b/chromium/services/navigation/content_client/content_browser_client.cc new file mode 100644 index 00000000000..b98432c4a0b --- /dev/null +++ b/chromium/services/navigation/content_client/content_browser_client.cc @@ -0,0 +1,52 @@ +// 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 "services/navigation/content_client/content_browser_client.h" + +#include <utility> + +#include "base/command_line.h" +#include "base/memory/ptr_util.h" +#include "content/public/common/mojo_shell_connection.h" +#include "content/shell/browser/shell_browser_context.h" +#include "services/navigation/content_client/browser_main_parts.h" +#include "services/navigation/navigation.h" + +namespace navigation { +namespace { + +class ConnectionListener : public content::MojoShellConnection::Listener { + public: + explicit ConnectionListener(std::unique_ptr<shell::ShellClient> wrapped) + : wrapped_(std::move(wrapped)) {} + + private: + bool AcceptConnection(shell::Connection* connection) override { + return wrapped_->AcceptConnection(connection); + } + + std::unique_ptr<shell::ShellClient> wrapped_; + + DISALLOW_COPY_AND_ASSIGN(ConnectionListener); +}; + +} // namespace + +ContentBrowserClient::ContentBrowserClient() {} +ContentBrowserClient::~ContentBrowserClient() {} + +content::BrowserMainParts* ContentBrowserClient::CreateBrowserMainParts( + const content::MainFunctionParams& parameters) { + browser_main_parts_ = new BrowserMainParts(parameters); + return browser_main_parts_; +} + +void ContentBrowserClient::AddMojoShellConnectionListeners() { + Navigation* navigation = new Navigation; + browser_main_parts_->set_navigation(navigation); + content::MojoShellConnection::Get()->AddListener( + base::WrapUnique(new ConnectionListener(base::WrapUnique(navigation)))); +} + +} // namespace navigation diff --git a/chromium/services/navigation/content_client/content_browser_client.h b/chromium/services/navigation/content_client/content_browser_client.h new file mode 100644 index 00000000000..ca30601bbac --- /dev/null +++ b/chromium/services/navigation/content_client/content_browser_client.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef SERVICES_NAVIGATION_CONTENT_CLIENT_CONTENT_BROWSER_CLIENT_H_ +#define SERVICES_NAVIGATION_CONTENT_CLIENT_CONTENT_BROWSER_CLIENT_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "content/public/browser/content_browser_client.h" + +namespace navigation { + +class BrowserMainParts; + +class ContentBrowserClient : public content::ContentBrowserClient { + public: + ContentBrowserClient(); + ~ContentBrowserClient() override; + + // Overridden from content::ContentBrowserClient: + content::BrowserMainParts* CreateBrowserMainParts( + const content::MainFunctionParams& parameters) override; + void AddMojoShellConnectionListeners() override; + + private: + BrowserMainParts* browser_main_parts_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ContentBrowserClient); +}; + +} // namespace navigation + +#endif // SERVICES_NAVIGATION_CONTENT_CLIENT_CONTENT_BROWSER_CLIENT_H_ diff --git a/chromium/services/navigation/content_client/main_delegate.cc b/chromium/services/navigation/content_client/main_delegate.cc new file mode 100644 index 00000000000..167c15dabc6 --- /dev/null +++ b/chromium/services/navigation/content_client/main_delegate.cc @@ -0,0 +1,44 @@ +// 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 "services/navigation/content_client/main_delegate.h" + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "content/public/common/content_switches.h" +#include "services/navigation/content_client/content_browser_client.h" +#include "ui/base/ime/input_method_initializer.h" +#include "ui/base/resource/resource_bundle.h" + +namespace navigation { + +MainDelegate::MainDelegate() {} +MainDelegate::~MainDelegate() {} + +bool MainDelegate::BasicStartupComplete(int* exit_code) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + + content::SetContentClient(&content_client_); + + return false; +} + +void MainDelegate::PreSandboxStartup() { + base::FilePath path; + PathService::Get(base::DIR_MODULE, &path); + base::FilePath pak_path = path.Append(FILE_PATH_LITERAL("navigation.pak")); + ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_path); + ui::InitializeInputMethodForTesting(); +} + +content::ContentBrowserClient* MainDelegate::CreateContentBrowserClient() { + browser_client_.reset(new ContentBrowserClient); + return browser_client_.get(); +} + +} // namespace navigation diff --git a/chromium/services/navigation/content_client/main_delegate.h b/chromium/services/navigation/content_client/main_delegate.h new file mode 100644 index 00000000000..455ee58f524 --- /dev/null +++ b/chromium/services/navigation/content_client/main_delegate.h @@ -0,0 +1,42 @@ +// 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. + +#ifndef SERVICES_NAVIGATION_CONTENT_CLIENT_MAIN_DELEGATE_H_ +#define SERVICES_NAVIGATION_CONTENT_CLIENT_MAIN_DELEGATE_H_ + +#include <memory> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "content/public/app/content_main_delegate.h" +#include "content/shell/common/shell_content_client.h" + +namespace content { +class ShellContentRendererClient; +class ShellContentUtilityClient; +} + +namespace navigation { + +class ContentBrowserClient; + +class MainDelegate : public content::ContentMainDelegate { + public: + MainDelegate(); + ~MainDelegate() override; + + bool BasicStartupComplete(int* exit_code) override; + void PreSandboxStartup() override; + content::ContentBrowserClient* CreateContentBrowserClient() override; + + private: + std::unique_ptr<ContentBrowserClient> browser_client_; + content::ShellContentClient content_client_; + + DISALLOW_COPY_AND_ASSIGN(MainDelegate); +}; + +} // namespace navigation + +#endif // SERVICES_NAVIGATION_CONTENT_CLIENT_MAIN_DELEGATE_H_ diff --git a/chromium/services/navigation/embedder_manifest.json b/chromium/services/navigation/embedder_manifest.json new file mode 100644 index 00000000000..ede6e1ef53b --- /dev/null +++ b/chromium/services/navigation/embedder_manifest.json @@ -0,0 +1,6 @@ +{ + "manifest_version": 1, + "name": "exe:navigation", + "display_name": "Navigation Embedder", + "capabilities": {} +} diff --git a/chromium/services/navigation/main.cc b/chromium/services/navigation/main.cc new file mode 100644 index 00000000000..942a6e0537b --- /dev/null +++ b/chromium/services/navigation/main.cc @@ -0,0 +1,53 @@ +// 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 "base/at_exit.h" +#include "base/command_line.h" +#include "base/debug/stack_trace.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "content/public/app/content_main.h" +#include "services/navigation/content_client/main_delegate.h" +#include "services/shell/runner/init.h" + +#if defined(OS_WIN) +#include "content/public/app/sandbox_helper_win.h" +#include "sandbox/win/src/sandbox_types.h" +#endif + +#if defined(OS_WIN) +int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) { + int argc = 0; + char** argv = nullptr; +#else +int main(int argc, const char** argv) { +#endif + base::CommandLine::Init(argc, argv); + shell::WaitForDebuggerIfNecessary(); + +#if !defined(OFFICIAL_BUILD) +#if defined(OS_WIN) + base::RouteStdioToConsole(false); +#endif +#endif + + base::FilePath log_filename; + PathService::Get(base::DIR_EXE, &log_filename); + log_filename = log_filename.AppendASCII("navigation.mojo.log"); + logging::LoggingSettings settings; + settings.logging_dest = logging::LOG_TO_ALL; + settings.log_file = log_filename.value().c_str(); + settings.delete_old = logging::DELETE_OLD_LOG_FILE; + logging::InitLogging(settings); + + navigation::MainDelegate delegate; + content::ContentMainParams params(&delegate); +#if defined(OS_WIN) + sandbox::SandboxInterfaceInfo sandbox_info = { 0 }; + content::InitializeSandboxInfo(&sandbox_info); + params.instance = GetModuleHandle(NULL); + params.sandbox_info = &sandbox_info; +#endif + return content::ContentMain(params); +} diff --git a/chromium/services/navigation/manifest.json b/chromium/services/navigation/manifest.json new file mode 100644 index 00000000000..c7940f550de --- /dev/null +++ b/chromium/services/navigation/manifest.json @@ -0,0 +1,14 @@ +{ + "manifest_version": 1, + "name": "exe:navigation", + "display_name": "Navigation", + "capabilities": { + "provided": { + "app": [ "navigation::mojom::ViewFactory" ] + }, + "required": { + "*": { "classes": [ "app" ] }, + "mojo:shell": { "classes": [ "shell:client_process", "shell:user_id", "shell:instance_name" ] } + } + } +} diff --git a/chromium/services/navigation/navigation.cc b/chromium/services/navigation/navigation.cc new file mode 100644 index 00000000000..75176d7c492 --- /dev/null +++ b/chromium/services/navigation/navigation.cc @@ -0,0 +1,54 @@ +// 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 "services/navigation/navigation.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "services/navigation/view_impl.h" + +namespace navigation { + +Navigation::Navigation() + : ref_factory_(base::MessageLoop::QuitWhenIdleClosure()) { + bindings_.set_connection_error_handler( + base::Bind(&Navigation::ViewFactoryLost, base::Unretained(this))); +} +Navigation::~Navigation() {} + +void Navigation::Init(shell::Connector* connector, + content::BrowserContext* browser_context) { + connector_ = connector; + browser_context_ = browser_context; + for (auto& pending : pending_creates_) + CreateView(std::move(pending.first), std::move(pending.second)); +} + +bool Navigation::AcceptConnection(shell::Connection* connection) { + connection->AddInterface<mojom::ViewFactory>(this); + return true; +} + +void Navigation::Create(shell::Connection* connection, + mojom::ViewFactoryRequest request) { + bindings_.AddBinding(this, std::move(request)); + refs_.insert(ref_factory_.CreateRef()); +} + +void Navigation::CreateView(mojom::ViewClientPtr client, + mojom::ViewRequest request) { + if (!browser_context_) { + pending_creates_.push_back( + std::make_pair(std::move(client), std::move(request))); + return; + } + new ViewImpl(connector_, browser_context_, std::move(client), + std::move(request), ref_factory_.CreateRef()); +} + +void Navigation::ViewFactoryLost() { + refs_.erase(refs_.begin()); +} + +} // navigation diff --git a/chromium/services/navigation/navigation.h b/chromium/services/navigation/navigation.h new file mode 100644 index 00000000000..cb9fb1f86a6 --- /dev/null +++ b/chromium/services/navigation/navigation.h @@ -0,0 +1,57 @@ +// 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. + +#ifndef SERVICES_NAVIGATION_NAVIGATION_H_ +#define SERVICES_NAVIGATION_NAVIGATION_H_ + +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/navigation/public/interfaces/view.mojom.h" +#include "services/shell/public/cpp/interface_factory.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/cpp/shell_connection_ref.h" + +namespace content { +class BrowserContext; +} + +namespace navigation { + +class Navigation : public shell::ShellClient, + public shell::InterfaceFactory<mojom::ViewFactory>, + public mojom::ViewFactory { + public: + Navigation(); + ~Navigation() override; + + void Init(shell::Connector* connector, + content::BrowserContext* browser_context); + + private: + // shell::ShellClient: + bool AcceptConnection(shell::Connection* connection) override; + + // shell::InterfaceFactory<mojom::ViewFactory>: + void Create(shell::Connection* connection, + mojom::ViewFactoryRequest request) override; + + // mojom::ViewFactory: + void CreateView(mojom::ViewClientPtr client, + mojom::ViewRequest request) override; + + void ViewFactoryLost(); + + shell::Connector* connector_ = nullptr; + shell::ShellConnectionRefFactory ref_factory_; + std::set<std::unique_ptr<shell::ShellConnectionRef>> refs_; + content::BrowserContext* browser_context_ = nullptr; + mojo::BindingSet<mojom::ViewFactory> bindings_; + std::vector<std::pair<mojom::ViewClientPtr, mojom::ViewRequest>> + pending_creates_; + + DISALLOW_COPY_AND_ASSIGN(Navigation); +}; + +} // navigation + +#endif // SERVICES_NAVIGATION_NAVIGATION_H_ diff --git a/chromium/services/navigation/navigation_unittest.cc b/chromium/services/navigation/navigation_unittest.cc new file mode 100644 index 00000000000..a6a7b0f6b4b --- /dev/null +++ b/chromium/services/navigation/navigation_unittest.cc @@ -0,0 +1,77 @@ +// 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 <memory> + +#include "base/macros.h" +#include "base/run_loop.h" +#include "services/navigation/public/interfaces/view.mojom.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/cpp/shell_test.h" + +namespace navigation { + +class NavigationTest : public shell::test::ShellTest, + public mojom::ViewClient { + public: + NavigationTest() + : shell::test::ShellTest("exe:navigation_unittests"), + binding_(this) {} + ~NavigationTest() override {} + + protected: + void SetUp() override { + shell::test::ShellTest::SetUp(); + window_manager_connection_ = connector()->Connect("mojo:test_wm"); + } + + mojom::ViewClientPtr GetViewClient() { + return binding_.CreateInterfacePtrAndBind(); + } + + void QuitOnLoadingStateChange(base::RunLoop* loop) { + loop_ = loop; + } + + private: + // mojom::ViewClient: + void LoadingStateChanged(bool is_loading) override { + // Should see loading start, then stop. + if (++load_count_ == 2 && loop_) + loop_->Quit(); + } + void NavigationStateChanged(const GURL& url, + const mojo::String& title, + bool can_go_back, + bool can_go_forward) override {} + void LoadProgressChanged(double progress) override {} + void ViewCreated(mojom::ViewPtr, + mojom::ViewClientRequest, + bool, + mojo::RectPtr, + bool) override {} + void Close() override {} + + int load_count_ = 0; + mojo::Binding<mojom::ViewClient> binding_; + base::RunLoop* loop_ = nullptr; + std::unique_ptr<shell::Connection> window_manager_connection_; + + DISALLOW_COPY_AND_ASSIGN(NavigationTest); +}; + +TEST_F(NavigationTest, Navigate) { + mojom::ViewFactoryPtr view_factory; + connector()->ConnectToInterface("exe:navigation", &view_factory); + + mojom::ViewPtr view; + view_factory->CreateView(GetViewClient(), GetProxy(&view)); + view->NavigateTo(GURL("about:blank")); + + base::RunLoop loop; + QuitOnLoadingStateChange(&loop); + loop.Run(); +} + +} // namespace navigation diff --git a/chromium/services/navigation/public/interfaces/BUILD.gn b/chromium/services/navigation/public/interfaces/BUILD.gn new file mode 100644 index 00000000000..ec88b94b405 --- /dev/null +++ b/chromium/services/navigation/public/interfaces/BUILD.gn @@ -0,0 +1,17 @@ +# 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { + sources = [ + "view.mojom", + ] + + deps = [ + "//components/mus/public/interfaces", + "//ui/mojo/geometry:interfaces", + "//url/mojo:url_mojom_gurl", + ] +} diff --git a/chromium/services/navigation/public/interfaces/view.mojom b/chromium/services/navigation/public/interfaces/view.mojom new file mode 100644 index 00000000000..0ceead80299 --- /dev/null +++ b/chromium/services/navigation/public/interfaces/view.mojom @@ -0,0 +1,42 @@ +// 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. + +module navigation.mojom; + +import "components/mus/public/interfaces/window_tree.mojom"; +import "ui/mojo/geometry/geometry.mojom"; +import "url/mojo/url.mojom"; + +interface ViewFactory { + CreateView(ViewClient client, View& view); +}; + +interface ViewClient { + LoadingStateChanged(bool is_loading); + NavigationStateChanged(url.mojom.Url url, + string title, + bool can_go_back, + bool can_go_forward); + LoadProgressChanged(double progress); + ViewCreated(View view, + ViewClient& client, + bool is_popup, + mojo.Rect initial_rect, + bool user_gesture); + Close(); +}; + +interface View { + // Navigates the view to |url|. + NavigateTo(url.mojom.Url url); + + GoBack(); + GoForward(); + Reload(bool skip_cache); + Stop(); + + // Obtains a Mus WindowTreeClient for the View, so it can be embedded in a + // UI. + GetWindowTreeClient(mus.mojom.WindowTreeClient& client); +}; diff --git a/chromium/services/navigation/unittest_manifest.json b/chromium/services/navigation/unittest_manifest.json new file mode 100644 index 00000000000..1040004a1c8 --- /dev/null +++ b/chromium/services/navigation/unittest_manifest.json @@ -0,0 +1,13 @@ +{ + "manifest_version": 1, + "name": "exe:navigation_unittests", + "display_name": "Navigation Unittests", + "capabilities": { + "required": { + "exe:navigation": { + "interfaces": [ "navigation::mojom::ViewFactory" ] + }, + "*": { "classes": [ "app" ] } + } + } +} diff --git a/chromium/services/navigation/view_impl.cc b/chromium/services/navigation/view_impl.cc new file mode 100644 index 00000000000..227b3580994 --- /dev/null +++ b/chromium/services/navigation/view_impl.cc @@ -0,0 +1,139 @@ +// 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 "services/navigation/view_impl.h" + +#include "base/strings/utf_string_conversions.h" +#include "components/mus/public/cpp/window_tree_connection.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/web_contents.h" +#include "mojo/converters/geometry/geometry_type_converters.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/mus/native_widget_mus.h" +#include "ui/views/widget/widget.h" +#include "url/gurl.h" + +namespace navigation { + +ViewImpl::ViewImpl(shell::Connector* connector, + content::BrowserContext* browser_context, + mojom::ViewClientPtr client, + mojom::ViewRequest request, + std::unique_ptr<shell::ShellConnectionRef> ref) + : connector_(connector), + binding_(this, std::move(request)), + client_(std::move(client)), + ref_(std::move(ref)), + web_view_(new views::WebView(browser_context)) { + web_view_->GetWebContents()->SetDelegate(this); +} +ViewImpl::~ViewImpl() {} + +void ViewImpl::NavigateTo(const GURL& url) { + web_view_->GetWebContents()->GetController().LoadURL( + url, content::Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); +} + +void ViewImpl::GoBack() { + web_view_->GetWebContents()->GetController().GoBack(); +} + +void ViewImpl::GoForward() { + web_view_->GetWebContents()->GetController().GoForward(); +} + +void ViewImpl::Reload(bool skip_cache) { + if (skip_cache) + web_view_->GetWebContents()->GetController().Reload(true); + else + web_view_->GetWebContents()->GetController().ReloadBypassingCache(true); +} + +void ViewImpl::Stop() { + web_view_->GetWebContents()->Stop(); +} + +void ViewImpl::GetWindowTreeClient( + mus::mojom::WindowTreeClientRequest request) { + mus::WindowTreeConnection::Create( + this, std::move(request), + mus::WindowTreeConnection::CreateType::DONT_WAIT_FOR_EMBED); +} + +void ViewImpl::AddNewContents(content::WebContents* source, + content::WebContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture, + bool* was_blocked) { + mojom::ViewClientPtr client; + mojom::ViewPtr view; + mojom::ViewRequest view_request = GetProxy(&view); + client_->ViewCreated(std::move(view), GetProxy(&client), + disposition == NEW_POPUP, mojo::Rect::From(initial_rect), + user_gesture); + ViewImpl* impl = + new ViewImpl(connector_, new_contents->GetBrowserContext(), + std::move(client), std::move(view_request), ref_->Clone()); + // TODO(beng): This is a bit crappy. should be able to create the ViewImpl + // with |new_contents| instead. + impl->web_view_->SetWebContents(new_contents); + impl->web_view_->GetWebContents()->SetDelegate(impl); + + // TODO(beng): this reply is currently synchronous, figure out a fix. + if (was_blocked) + *was_blocked = false; +} + +void ViewImpl::CloseContents(content::WebContents* source) { + client_->Close(); +} + +void ViewImpl::LoadingStateChanged(content::WebContents* source, + bool to_different_document) { + client_->LoadingStateChanged(source->IsLoading()); +} + +void ViewImpl::NavigationStateChanged(content::WebContents* source, + content::InvalidateTypes changed_flags) { + client_->NavigationStateChanged(source->GetVisibleURL(), + base::UTF16ToUTF8(source->GetTitle()), + source->GetController().CanGoBack(), + source->GetController().CanGoForward()); +} + +void ViewImpl::LoadProgressChanged(content::WebContents* source, + double progress) { + client_->LoadProgressChanged(progress); +} + +void ViewImpl::OnEmbed(mus::Window* root) { + DCHECK(!widget_.get()); + widget_.reset(new views::Widget); + views::Widget::InitParams params( + views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.delegate = this; + params.native_widget = new views::NativeWidgetMus( + widget_.get(), connector_, root, mus::mojom::SurfaceType::DEFAULT); + widget_->Init(params); + widget_->Show(); +} + +void ViewImpl::OnConnectionLost(mus::WindowTreeConnection* connection) {} +void ViewImpl::OnEventObserved(const ui::Event& event, mus::Window* target) {} + +views::View* ViewImpl::GetContentsView() { + return web_view_; +} + +views::Widget* ViewImpl::GetWidget() { + return web_view_->GetWidget(); +} + +const views::Widget* ViewImpl::GetWidget() const { + return web_view_->GetWidget(); +} + +} // navigation diff --git a/chromium/services/navigation/view_impl.h b/chromium/services/navigation/view_impl.h new file mode 100644 index 00000000000..426185f7c9c --- /dev/null +++ b/chromium/services/navigation/view_impl.h @@ -0,0 +1,89 @@ +// 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. + +#ifndef SERVICES_NAVIGATION_VIEW_IMPL_H_ +#define SERVICES_NAVIGATION_VIEW_IMPL_H_ + +#include "base/macros.h" +#include "components/mus/public/cpp/window_tree_delegate.h" +#include "content/public/browser/web_contents_delegate.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/navigation/public/interfaces/view.mojom.h" +#include "services/shell/public/cpp/interface_factory.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/cpp/shell_connection_ref.h" +#include "ui/views/widget/widget_delegate.h" + +namespace views { +class WebView; +class Widget; +} + +namespace navigation { + +class ViewImpl : public mojom::View, + public content::WebContentsDelegate, + public mus::WindowTreeDelegate, + public views::WidgetDelegate { + public: + ViewImpl(shell::Connector* connector, + content::BrowserContext* browser_context, + mojom::ViewClientPtr client, + mojom::ViewRequest request, + std::unique_ptr<shell::ShellConnectionRef> ref); + ~ViewImpl() override; + + private: + // mojom::View: + void NavigateTo(const GURL& url) override; + void GoBack() override; + void GoForward() override; + void Reload(bool skip_cache) override; + void Stop() override; + void GetWindowTreeClient( + mus::mojom::WindowTreeClientRequest request) override; + + // content::WebContentsDelegate: + void AddNewContents(content::WebContents* source, + content::WebContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture, + bool* was_blocked) override; + void CloseContents(content::WebContents* source) override; + void LoadingStateChanged(content::WebContents* source, + bool to_different_document) override; + void NavigationStateChanged(content::WebContents* source, + content::InvalidateTypes changed_flags) override; + void LoadProgressChanged(content::WebContents* source, + double progress) override; + + // mus::WindowTreeDelegate: + void OnEmbed(mus::Window* root) override; + void OnConnectionLost(mus::WindowTreeConnection* connection) override; + void OnEventObserved(const ui::Event& event, mus::Window* target) override; + + // views::WidgetDelegate: + views::View* GetContentsView() override; + views::Widget* GetWidget() override; + const views::Widget* GetWidget() const override; + + shell::Connector* connector_; + mojo::StrongBinding<mojom::View> binding_; + mojom::ViewClientPtr client_; + std::unique_ptr<shell::ShellConnectionRef> ref_; + + views::WebView* web_view_; + + std::unique_ptr<content::WebContents> web_contents_; + + std::unique_ptr<views::Widget> widget_; + + DISALLOW_COPY_AND_ASSIGN(ViewImpl); +}; + +} // navigation + +#endif // SERVICES_NAVIGATION_VIEW_IMPL_H_ diff --git a/chromium/services/shell/BUILD.gn b/chromium/services/shell/BUILD.gn new file mode 100644 index 00000000000..a854f36aaec --- /dev/null +++ b/chromium/services/shell/BUILD.gn @@ -0,0 +1,58 @@ +# Copyright 2014 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. + +import("//mojo/public/mojo_application_manifest.gni") +import("//mojo/public/tools/bindings/mojom.gni") +import("//testing/test.gni") + +group("all") { + testonly = true + deps = [ + ":shell", + "//services/shell/background", + "//services/shell/runner", + "//services/shell/standalone", + "//services/shell/tests", + ] +} + +source_set("shell") { + output_name = "mojo_shell" + sources = [ + "connect_params.cc", + "connect_params.h", + "connect_util.cc", + "connect_util.h", + "native_runner.h", + "native_runner_delegate.h", + "shell.cc", + "shell.h", + "switches.cc", + "switches.h", + ] + + deps = [ + "//base", + "//base/third_party/dynamic_annotations", + "//mojo/common", + "//mojo/public/cpp/bindings", + "//services/catalog/public/interfaces", + "//services/shell/public/cpp:sources", + "//services/shell/public/interfaces", + ] + + public_deps = [ + # ApplicationManager exposes and uses PackageManager types in its header. + "//services/catalog:lib", + ] + + data_deps = [ + ":manifest", + ] +} + +mojo_application_manifest("manifest") { + application_name = "shell" + source = "manifest.json" +} diff --git a/chromium/services/shell/OWNERS b/chromium/services/shell/OWNERS new file mode 100644 index 00000000000..5bd620a16dc --- /dev/null +++ b/chromium/services/shell/OWNERS @@ -0,0 +1,5 @@ +amistry@chromium.org +ben@chromium.org +jam@chromium.org +rockot@chromium.org +sky@chromium.org diff --git a/chromium/services/shell/background/BUILD.gn b/chromium/services/shell/background/BUILD.gn new file mode 100644 index 00000000000..067f6eb58db --- /dev/null +++ b/chromium/services/shell/background/BUILD.gn @@ -0,0 +1,42 @@ +# 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. + +group("background") { + testonly = true + deps = [ + ":lib", + ":main", + ] +} + +source_set("lib") { + sources = [ + "background_shell.cc", + "background_shell.h", + ] + deps = [ + "//base", + "//services/catalog:lib", + "//services/shell", + "//services/shell/public/cpp:sources", + "//services/shell/runner:init", + "//services/shell/standalone:lib", + ] +} + +source_set("main") { + sources = [ + "background_shell_main.cc", + "background_shell_main.h", + ] + deps = [ + "//base", + "//services/shell", + "//services/shell/public/cpp:sources", + "//services/shell/runner:init", + "//services/shell/runner/common", + "//services/shell/runner/host:lib", + "//services/shell/standalone:lib", + ] +} diff --git a/chromium/services/shell/background/DEPS b/chromium/services/shell/background/DEPS new file mode 100644 index 00000000000..3fd6080bf1e --- /dev/null +++ b/chromium/services/shell/background/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+services/catalog", +] diff --git a/chromium/services/shell/background/background_shell.cc b/chromium/services/shell/background/background_shell.cc new file mode 100644 index 00000000000..4c42c09dda5 --- /dev/null +++ b/chromium/services/shell/background/background_shell.cc @@ -0,0 +1,174 @@ +// 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 "services/shell/background/background_shell.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_pump_default.h" +#include "base/path_service.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/simple_thread.h" +#include "services/catalog/store.h" +#include "services/shell/connect_params.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/cpp/shell_connection.h" +#include "services/shell/shell.h" +#include "services/shell/standalone/context.h" + +namespace shell { + +namespace { + +std::unique_ptr<base::MessagePump> CreateDefaultMessagePump() { + return base::WrapUnique(new base::MessagePumpDefault); +} + +class MojoMessageLoop : public base::MessageLoop { + public: + MojoMessageLoop() + : base::MessageLoop(base::MessageLoop::TYPE_CUSTOM, + base::Bind(&CreateDefaultMessagePump)) {} + ~MojoMessageLoop() override {} + + void BindToCurrentThread() { base::MessageLoop::BindToCurrentThread(); } + + private: + DISALLOW_COPY_AND_ASSIGN(MojoMessageLoop); +}; + +} // namespace + +// Manages the thread to startup mojo. +class BackgroundShell::MojoThread : public base::SimpleThread { + public: + explicit MojoThread(std::unique_ptr<BackgroundShell::InitParams> init_params) + : SimpleThread("mojo-background-shell"), + init_params_(std::move(init_params)) {} + ~MojoThread() override {} + + void CreateShellClientRequest(base::WaitableEvent* signal, + const std::string& name, + mojom::ShellClientRequest* request) { + // Only valid to call this on the background thread. + DCHECK_EQ(message_loop_, base::MessageLoop::current()); + *request = context_->shell()->InitInstanceForEmbedder(name); + signal->Signal(); + } + + void Connect(std::unique_ptr<ConnectParams> params) { + context_->shell()->Connect(std::move(params)); + } + + base::MessageLoop* message_loop() { return message_loop_; } + + // Stops the background thread. + void Stop() { + DCHECK_NE(message_loop_, base::MessageLoop::current()); + message_loop_->task_runner()->PostTask( + FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + Join(); + } + + void RunShellCallback(const BackgroundShell::ShellThreadCallback& callback) { + DCHECK_EQ(message_loop_, base::MessageLoop::current()); + callback.Run(context_->shell()); + } + + // base::SimpleThread: + void Start() override { + DCHECK(!message_loop_); + message_loop_ = new MojoMessageLoop; + base::SimpleThread::Start(); + } + void Run() override { + // The construction/destruction order is very finicky and has to be done + // in the order here. + std::unique_ptr<base::MessageLoop> message_loop(message_loop_); + + std::unique_ptr<Context::InitParams> context_init_params( + new Context::InitParams); + if (init_params_) { + context_init_params->catalog_store = + std::move(init_params_->catalog_store); + context_init_params->native_runner_delegate = + init_params_->native_runner_delegate; + context_init_params->init_edk = init_params_->init_edk; + } + if (context_init_params->init_edk) + Context::EnsureEmbedderIsInitialized(); + + message_loop_->BindToCurrentThread(); + + std::unique_ptr<Context> context(new Context); + context_ = context.get(); + context_->Init(std::move(context_init_params)); + + message_loop_->Run(); + + // Has to happen after run, but while messageloop still valid. + context_->Shutdown(); + + // Context has to be destroyed after the MessageLoop has been destroyed. + message_loop.reset(); + context_ = nullptr; + } + + private: + // We own this. It's created on the main thread, but destroyed on the + // background thread. + MojoMessageLoop* message_loop_ = nullptr; + // Created in Run() on the background thread. + Context* context_ = nullptr; + + std::unique_ptr<BackgroundShell::InitParams> init_params_; + + DISALLOW_COPY_AND_ASSIGN(MojoThread); +}; + +BackgroundShell::InitParams::InitParams() {} +BackgroundShell::InitParams::~InitParams() {} + +BackgroundShell::BackgroundShell() {} + +BackgroundShell::~BackgroundShell() { + thread_->Stop(); +} + +void BackgroundShell::Init(std::unique_ptr<InitParams> init_params) { + DCHECK(!thread_); + thread_.reset(new MojoThread(std::move(init_params))); + thread_->Start(); +} + +mojom::ShellClientRequest BackgroundShell::CreateShellClientRequest( + const std::string& name) { + std::unique_ptr<ConnectParams> params(new ConnectParams); + params->set_source(CreateShellIdentity()); + params->set_target(Identity(name, mojom::kRootUserID)); + mojom::ShellClientRequest request; + base::WaitableEvent signal(true, false); + thread_->message_loop()->task_runner()->PostTask( + FROM_HERE, base::Bind(&MojoThread::CreateShellClientRequest, + base::Unretained(thread_.get()), &signal, name, + &request)); + signal.Wait(); + thread_->message_loop()->task_runner()->PostTask( + FROM_HERE, base::Bind(&MojoThread::Connect, + base::Unretained(thread_.get()), + base::Passed(¶ms))); + return request; +} + +void BackgroundShell::ExecuteOnShellThread( + const ShellThreadCallback& callback) { + thread_->message_loop()->task_runner()->PostTask( + FROM_HERE, base::Bind(&MojoThread::RunShellCallback, + base::Unretained(thread_.get()), callback)); +} + +} // namespace shell diff --git a/chromium/services/shell/background/background_shell.h b/chromium/services/shell/background/background_shell.h new file mode 100644 index 00000000000..8b48dd53794 --- /dev/null +++ b/chromium/services/shell/background/background_shell.h @@ -0,0 +1,67 @@ +// 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. + +#ifndef SERVICES_SHELL_BACKGROUND_BACKGROUND_SHELL_H_ +#define SERVICES_SHELL_BACKGROUND_BACKGROUND_SHELL_H_ + +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/catalog/store.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" + +namespace catalog { +class Store; +} + +namespace shell { + +class NativeRunnerDelegate; +class Shell; + +// BackgroundShell starts up the mojo shell on a background thread, and +// destroys the thread in the destructor. Once created use CreateApplication() +// to obtain an InterfaceRequest for the Application. The InterfaceRequest can +// then be bound to an ApplicationImpl. +class BackgroundShell { + public: + struct InitParams { + InitParams(); + ~InitParams(); + + NativeRunnerDelegate* native_runner_delegate = nullptr; + std::unique_ptr<catalog::Store> catalog_store; + // If true the edk is initialized. + bool init_edk = true; + }; + + BackgroundShell(); + ~BackgroundShell(); + + // Starts the background shell. |command_line_switches| are additional + // switches applied to any processes spawned by this call. + void Init(std::unique_ptr<InitParams> init_params); + + // Obtains an InterfaceRequest for the specified name. + mojom::ShellClientRequest CreateShellClientRequest( + const std::string& name); + + // Use to do processing on the thread running the shell. The callback is + // supplied a pointer to the Shell. The callback does *not* own the Shell. + using ShellThreadCallback = base::Callback<void(Shell*)>; + void ExecuteOnShellThread(const ShellThreadCallback& callback); + + private: + class MojoThread; + + std::unique_ptr<MojoThread> thread_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundShell); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_BACKGROUND_BACKGROUND_SHELL_H_ diff --git a/chromium/services/shell/background/background_shell_main.cc b/chromium/services/shell/background/background_shell_main.cc new file mode 100644 index 00000000000..e8d7b21c32f --- /dev/null +++ b/chromium/services/shell/background/background_shell_main.cc @@ -0,0 +1,41 @@ +// 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 "services/shell/background/background_shell_main.h" + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/debug/debugger.h" +#include "base/process/launch.h" +#include "services/shell/runner/common/switches.h" +#include "services/shell/runner/host/child_process.h" +#include "services/shell/runner/init.h" + +namespace shell { +namespace { + +int RunChildProcess() { + base::AtExitManager at_exit; + InitializeLogging(); + WaitForDebuggerIfNecessary(); +#if !defined(OFFICIAL_BUILD) && defined(OS_WIN) + base::RouteStdioToConsole(false); +#endif + return ChildProcessMain(); +} + +} // namespace +} // namespace shell + +int main(int argc, char** argv) { + base::CommandLine::Init(argc, argv); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kChildProcess)) { + return shell::RunChildProcess(); + } + // Reset CommandLine as most likely main() is going to use CommandLine too + // and expect to be able to initialize it. + base::CommandLine::Reset(); + return MasterProcessMain(argc, argv); +} diff --git a/chromium/services/shell/background/background_shell_main.h b/chromium/services/shell/background/background_shell_main.h new file mode 100644 index 00000000000..1475c357dea --- /dev/null +++ b/chromium/services/shell/background/background_shell_main.h @@ -0,0 +1,12 @@ +// 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. + +#ifndef SERVICES_SHELL_BACKGROUND_BACKGROUND_SHELL_MAIN_H_ +#define SERVICES_SHELL_BACKGROUND_BACKGROUND_SHELL_MAIN_H_ + +// The "main" gn target supplies a file with a main() that calls to the child +// process as necessary. For the main process this function is called. +int MasterProcessMain(int argc, char** argv); + +#endif // SERVICES_SHELL_BACKGROUND_BACKGROUND_SHELL_MAIN_H_ diff --git a/chromium/services/shell/connect_params.cc b/chromium/services/shell/connect_params.cc new file mode 100644 index 00000000000..d59ba1fcd44 --- /dev/null +++ b/chromium/services/shell/connect_params.cc @@ -0,0 +1,12 @@ +// 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 "services/shell/connect_params.h" + +namespace shell { + +ConnectParams::ConnectParams() {} +ConnectParams::~ConnectParams() {} + +} // namespace shell diff --git a/chromium/services/shell/connect_params.h b/chromium/services/shell/connect_params.h new file mode 100644 index 00000000000..7bd2ad5f413 --- /dev/null +++ b/chromium/services/shell/connect_params.h @@ -0,0 +1,77 @@ +// 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. + +#ifndef SERVICES_SHELL_CONNECT_PARAMS_H_ +#define SERVICES_SHELL_CONNECT_PARAMS_H_ + +#include <string> +#include <utility> + +#include "base/callback.h" +#include "base/macros.h" +#include "services/shell/public/cpp/identity.h" +#include "services/shell/public/interfaces/connector.mojom.h" +#include "services/shell/public/interfaces/interface_provider.mojom.h" + +namespace shell { + +// This class represents a request for the application manager to connect to an +// application. +class ConnectParams { + public: + ConnectParams(); + ~ConnectParams(); + + void set_source(const Identity& source) { source_ = source; } + const Identity& source() const { return source_; } + void set_target(const Identity& target) { target_ = target; } + const Identity& target() const { return target_; } + + void set_remote_interfaces(mojom::InterfaceProviderRequest value) { + remote_interfaces_ = std::move(value); + } + mojom::InterfaceProviderRequest TakeRemoteInterfaces() { + return std::move(remote_interfaces_); + } + + void set_local_interfaces(mojom::InterfaceProviderPtr value) { + local_interfaces_ = std::move(value); + } + mojom::InterfaceProviderPtr TakeLocalInterfaces() { + return std::move(local_interfaces_); + } + + void set_client_process_connection( + mojom::ClientProcessConnectionPtr client_process_connection) { + client_process_connection_ = std::move(client_process_connection); + } + mojom::ClientProcessConnectionPtr TakeClientProcessConnection() { + return std::move(client_process_connection_); + } + + void set_connect_callback(const mojom::Connector::ConnectCallback& value) { + connect_callback_ = value; + } + const mojom::Connector::ConnectCallback& connect_callback() const { + return connect_callback_; + } + + private: + // It may be null (i.e., is_null() returns true) which indicates that there is + // no source (e.g., for the first application or in tests). + Identity source_; + // The identity of the application being connected to. + Identity target_; + + mojom::InterfaceProviderRequest remote_interfaces_; + mojom::InterfaceProviderPtr local_interfaces_; + mojom::ClientProcessConnectionPtr client_process_connection_; + mojom::Connector::ConnectCallback connect_callback_; + + DISALLOW_COPY_AND_ASSIGN(ConnectParams); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_CONNECT_PARAMS_H_ diff --git a/chromium/services/shell/connect_util.cc b/chromium/services/shell/connect_util.cc new file mode 100644 index 00000000000..45b468ec102 --- /dev/null +++ b/chromium/services/shell/connect_util.cc @@ -0,0 +1,31 @@ +// 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 "services/shell/connect_util.h" + +#include <memory> +#include <utility> + +#include "services/shell/connect_params.h" +#include "services/shell/shell.h" + +namespace shell { + +mojo::ScopedMessagePipeHandle ConnectToInterfaceByName( + Shell* shell, + const Identity& source, + const Identity& target, + const std::string& interface_name) { + mojom::InterfaceProviderPtr remote_interfaces; + std::unique_ptr<ConnectParams> params(new ConnectParams); + params->set_source(source); + params->set_target(target); + params->set_remote_interfaces(mojo::GetProxy(&remote_interfaces)); + shell->Connect(std::move(params)); + mojo::MessagePipe pipe; + remote_interfaces->GetInterface(interface_name, std::move(pipe.handle1)); + return std::move(pipe.handle0); +} + +} // namespace shell diff --git a/chromium/services/shell/connect_util.h b/chromium/services/shell/connect_util.h new file mode 100644 index 00000000000..d5602251ac9 --- /dev/null +++ b/chromium/services/shell/connect_util.h @@ -0,0 +1,47 @@ +// 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. + +#ifndef SERVICES_SHELL_CONNECT_UTIL_H_ +#define SERVICES_SHELL_CONNECT_UTIL_H_ + +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/system/handle.h" +#include "services/shell/public/cpp/identity.h" +#include "services/shell/public/interfaces/connector.mojom.h" + +namespace shell { + +class Shell; + +mojo::ScopedMessagePipeHandle ConnectToInterfaceByName( + Shell* shell, + const Identity& source, + const Identity& target, + const std::string& interface_name); + +// Must only be used by shell internals and test code as it does not forward +// capability filters. Runs |name| with a permissive capability filter. +template <typename Interface> +inline void ConnectToInterface(Shell* shell, + const Identity& source, + const Identity& target, + mojo::InterfacePtr<Interface>* ptr) { + mojo::ScopedMessagePipeHandle service_handle = + ConnectToInterfaceByName(shell, source, target, Interface::Name_); + ptr->Bind(mojo::InterfacePtrInfo<Interface>(std::move(service_handle), 0u)); +} + +template <typename Interface> +inline void ConnectToInterface(Shell* shell, + const Identity& source, + const std::string& name, + mojo::InterfacePtr<Interface>* ptr) { + mojo::ScopedMessagePipeHandle service_handle = ConnectToInterfaceByName( + shell, source, Identity(name, mojom::kInheritUserID), Interface::Name_); + ptr->Bind(mojo::InterfacePtrInfo<Interface>(std::move(service_handle), 0u)); +} + +} // namespace shell + +#endif // SERVICES_SHELL_CONNECT_UTIL_H_ diff --git a/chromium/services/shell/manifest.json b/chromium/services/shell/manifest.json new file mode 100644 index 00000000000..bf91a389981 --- /dev/null +++ b/chromium/services/shell/manifest.json @@ -0,0 +1,29 @@ +{ + "manifest_version": 1, + "name": "mojo:shell", + "display_name": "Service Manager", + "capabilities": { + "provided": { + // Clients requesting this class are able to connect to other clients as + // specific users other than their own. + "shell:user_id": [ ], + // Clients requesting this class are allowed to register clients for + // processes they launch themselves. + "shell:client_process": [ ], + // Clients requesting this class are allowed to connect to other clients + // in specific process instance groups. + "shell:instance_name": [ ], + // Clients requesting this class are run as a unique user id which is + // visible to clients run as any user. + "shell:all_users": [ ], + "shell:block_wildcard": [ ], + // Clients requesting this class block inbound requests to bind interfaces + // from other sources who specify wildcard rules in their manifest + // capability interface sets. + "shell:explicit_class": [ ] + }, + "required": { + "mojo:shell": { "classes": [ "shell:all_users" ] } + } + } +} diff --git a/chromium/services/shell/mojo_shell_unittests.isolate b/chromium/services/shell/mojo_shell_unittests.isolate new file mode 100644 index 00000000000..4259f2ba925 --- /dev/null +++ b/chromium/services/shell/mojo_shell_unittests.isolate @@ -0,0 +1,23 @@ +# 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. +{ + 'includes': [ + '../../base/base.isolate', + ], + 'conditions': [ + ['OS=="win" or OS=="mac" or OS=="linux"', { + 'variables': { + 'command': [ + '../../testing/test_env.py', + '<(PRODUCT_DIR)/mojo_shell_unittests<(EXECUTABLE_SUFFIX)', + '--brave-new-test-launcher', + '--test-launcher-bot-mode', + ], + 'files': [ + '../../testing/test_env.py', + ], + }, + }], + ], +} diff --git a/chromium/services/shell/native_runner.h b/chromium/services/shell/native_runner.h new file mode 100644 index 00000000000..c81d2adf93c --- /dev/null +++ b/chromium/services/shell/native_runner.h @@ -0,0 +1,48 @@ +// 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. + +#ifndef SERVICES_SHELL_NATIVE_RUNNER_H_ +#define SERVICES_SHELL_NATIVE_RUNNER_H_ + +#include <memory> + +#include "base/callback_forward.h" +#include "base/process/process_handle.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" + +namespace base { +class FilePath; +} + +namespace shell { +class Identity; + +// Shell requires implementations of NativeRunner and NativeRunnerFactory to run +// native applications. +class NativeRunner { + public: + virtual ~NativeRunner() {} + + // Loads the app in the file at |app_path| and runs it on some other + // thread/process. Returns a ShellClient handle the shell can use to connect + // to the the app. + virtual mojom::ShellClientPtr Start( + const base::FilePath& app_path, + const Identity& target, + bool start_sandboxed, + const base::Callback<void(base::ProcessId)>& pid_available_callback, + const base::Closure& app_completed_callback) = 0; +}; + +class NativeRunnerFactory { + public: + virtual ~NativeRunnerFactory() {} + virtual std::unique_ptr<NativeRunner> Create( + const base::FilePath& app_path) = 0; +}; + +} // namespace shell + +#endif // SERVICES_SHELL_NATIVE_RUNNER_H_ diff --git a/chromium/services/shell/native_runner_delegate.h b/chromium/services/shell/native_runner_delegate.h new file mode 100644 index 00000000000..8c22d5a52b6 --- /dev/null +++ b/chromium/services/shell/native_runner_delegate.h @@ -0,0 +1,29 @@ +// 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. + +#ifndef SERVICES_SHELL_NATIVE_RUNNER_DELEGATE_H_ +#define SERVICES_SHELL_NATIVE_RUNNER_DELEGATE_H_ + +namespace base { +class CommandLine; +} + +namespace shell { +class Identity; + +class NativeRunnerDelegate { + public: + // Called to adjust the commandline for launching the specified app. + // WARNING: this is called on a background thread. + virtual void AdjustCommandLineArgumentsForTarget( + const Identity& target, + base::CommandLine* command_line) = 0; + + protected: + virtual ~NativeRunnerDelegate() {} +}; + +} // namespace shell + +#endif // SERVICES_SHELL_NATIVE_RUNNER_DELEGATE_H_ diff --git a/chromium/services/shell/public/cpp/BUILD.gn b/chromium/services/shell/public/cpp/BUILD.gn new file mode 100644 index 00000000000..cbfac3a258f --- /dev/null +++ b/chromium/services/shell/public/cpp/BUILD.gn @@ -0,0 +1,89 @@ +# Copyright 2014 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. + +# GYP version: mojo/mojo_base.gyp:mojo_application_base +source_set("cpp") { + public_deps = [ + ":sources", + ] +} + +# TODO(rockot): Rename this to "cpp". +source_set("sources") { + sources = [ + "application_runner.h", + "capabilities.h", + "connect.h", + "connection.h", + "connector.h", + "identity.h", + "interface_binder.h", + "interface_factory.h", + "interface_factory_impl.h", + "interface_registry.h", + "lib/application_runner.cc", + "lib/capabilities.cc", + "lib/connection_impl.cc", + "lib/connection_impl.h", + "lib/connector_impl.cc", + "lib/connector_impl.h", + "lib/identity.cc", + "lib/interface_factory_binder.h", + "lib/interface_registry.cc", + "lib/names.cc", + "lib/shell_client.cc", + "lib/shell_connection.cc", + "lib/shell_connection_ref.cc", + "names.h", + "shell_client.h", + "shell_connection.h", + "shell_connection_ref.h", + ] + + public_deps = [ + "//base", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/system", + "//services/shell/public/interfaces", + "//url", + ] +} + +source_set("application_support") { + sources = [ + "lib/init_commandline.cc", + "lib/initialize_base_and_icu.cc", + ] + + deps = [ + "//base", + "//base:i18n", + "//mojo/public/c/system", + ] +} + +source_set("shell_test_support") { + testonly = true + sources = [ + "lib/shell_test.cc", + "shell_test.h", + ] + + public_deps = [ + ":cpp", + "//testing/gtest", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//mojo/logging", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/system", + "//services/shell/background:lib", + "//services/shell/public/interfaces:interfaces_cpp_sources", + ] + + data_deps = [] +} diff --git a/chromium/services/shell/public/cpp/application_runner.h b/chromium/services/shell/public/cpp/application_runner.h new file mode 100644 index 00000000000..36d79e36fa4 --- /dev/null +++ b/chromium/services/shell/public/cpp/application_runner.h @@ -0,0 +1,76 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_APPLICATION_RUNNER_H_ +#define SERVICES_SHELL_PUBLIC_CPP_APPLICATION_RUNNER_H_ + +#include <memory> + +#include "base/message_loop/message_loop.h" +#include "mojo/public/cpp/system/core.h" + +namespace shell { + +class ShellClient; +class ShellConnection; + +// A utility for running a chromium based mojo Application. The typical use +// case is to use when writing your MojoMain: +// +// MojoResult MojoMain(MojoHandle shell_handle) { +// shell::ApplicationRunner runner(new MyDelegate()); +// return runner.Run(shell_handle); +// } +// +// ApplicationRunner takes care of chromium environment initialization and +// shutdown, and starting a MessageLoop from which your application can run and +// ultimately Quit(). +class ApplicationRunner { + public: + // Takes ownership of |client|. + explicit ApplicationRunner(ShellClient* client); + ~ApplicationRunner(); + + static void InitBaseCommandLine(); + + void set_message_loop_type(base::MessageLoop::Type type); + + // Once the various parameters have been set above, use Run to initialize an + // ShellConnection wired to the provided delegate, and run a MessageLoop until + // the application exits. + // + // Iff |init_base| is true, the runner will perform some initialization of + // base globals (e.g. CommandLine and AtExitManager) before starting the + // application. + MojoResult Run(MojoHandle shell_handle, bool init_base); + + // Calls Run above with |init_base| set to |true|. + MojoResult Run(MojoHandle shell_handle); + + // Allows the caller to shut down the connection with the shell. After the + // shell notices the pipe has closed, it will no longer track an instance of + // this application, though this application may continue to run and service + // requests from others. + void DestroyShellConnection(); + + // Allows the caller to explicitly quit the application. Must be called from + // the thread which created the ApplicationRunner. + void Quit(); + + private: + std::unique_ptr<ShellConnection> connection_; + std::unique_ptr<ShellClient> client_; + + // MessageLoop type. TYPE_CUSTOM is default (MessagePumpMojo will be used as + // the underlying message pump). + base::MessageLoop::Type message_loop_type_; + // Whether Run() has been called. + bool has_run_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationRunner); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_APPLICATION_RUNNER_H_ diff --git a/chromium/services/shell/public/cpp/capabilities.h b/chromium/services/shell/public/cpp/capabilities.h new file mode 100644 index 00000000000..53eaf7d2b12 --- /dev/null +++ b/chromium/services/shell/public/cpp/capabilities.h @@ -0,0 +1,77 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_CAPABILITIES_H_ +#define SERVICES_SHELL_PUBLIC_CPP_CAPABILITIES_H_ + +#include <map> +#include <set> +#include <string> + +#include "services/shell/public/interfaces/shell_resolver.mojom.h" + +namespace shell { + +using Class = std::string; +using Classes = std::set<std::string>; +using Interface = std::string; +using Interfaces = std::set<std::string>; +using Name = std::string; + +// See comments in services/shell/public/interfaces/capabilities.mojom for a +// description of CapabilityRequest and CapabilitySpec. + +struct CapabilityRequest { + CapabilityRequest(); + CapabilityRequest(const CapabilityRequest& other); + ~CapabilityRequest(); + bool operator==(const CapabilityRequest& other) const; + bool operator<(const CapabilityRequest& other) const; + Classes classes; + Interfaces interfaces; +}; + +struct CapabilitySpec { + CapabilitySpec(); + CapabilitySpec(const CapabilitySpec& other); + ~CapabilitySpec(); + bool operator==(const CapabilitySpec& other) const; + bool operator<(const CapabilitySpec& other) const; + std::map<Class, Interfaces> provided; + std::map<Name, CapabilityRequest> required; +}; + +} // namespace shell + +namespace mojo { + +template <> +struct TypeConverter<shell::mojom::CapabilitySpecPtr, shell::CapabilitySpec> { + static shell::mojom::CapabilitySpecPtr Convert( + const shell::CapabilitySpec& input); +}; + +template <> +struct TypeConverter<shell::CapabilitySpec, shell::mojom::CapabilitySpecPtr> { + static shell::CapabilitySpec Convert( + const shell::mojom::CapabilitySpecPtr& input); +}; + +template <> +struct TypeConverter<shell::mojom::CapabilityRequestPtr, + shell::CapabilityRequest> { + static shell::mojom::CapabilityRequestPtr Convert( + const shell::CapabilityRequest& input); +}; + +template <> +struct TypeConverter<shell::CapabilityRequest, + shell::mojom::CapabilityRequestPtr> { + static shell::CapabilityRequest Convert( + const shell::mojom::CapabilityRequestPtr& input); +}; + +} // namespace mojo + +#endif // SERVICES_SHELL_PUBLIC_CPP_CAPABILITIES_H_ diff --git a/chromium/services/shell/public/cpp/connect.h b/chromium/services/shell/public/cpp/connect.h new file mode 100644 index 00000000000..ecce18b9ef6 --- /dev/null +++ b/chromium/services/shell/public/cpp/connect.h @@ -0,0 +1,25 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_CONNECT_H_ +#define SERVICES_SHELL_PUBLIC_CPP_CONNECT_H_ + +#include <utility> + +#include "services/shell/public/interfaces/interface_provider.mojom.h" + +namespace shell { + +// Binds |ptr| to a remote implementation of Interface from |interfaces|. +template <typename Interface> +inline void GetInterface(mojom::InterfaceProvider* interfaces, + mojo::InterfacePtr<Interface>* ptr) { + mojo::MessagePipe pipe; + ptr->Bind(mojo::InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u)); + interfaces->GetInterface(Interface::Name_, std::move(pipe.handle1)); +} + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_CONNECT_H_ diff --git a/chromium/services/shell/public/cpp/connection.h b/chromium/services/shell/public/cpp/connection.h new file mode 100644 index 00000000000..989af5486f6 --- /dev/null +++ b/chromium/services/shell/public/cpp/connection.h @@ -0,0 +1,147 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_CONNECTION_H_ +#define SERVICES_SHELL_PUBLIC_CPP_CONNECTION_H_ + +#include <stdint.h> + +#include <string> +#include <utility> + +#include "base/memory/weak_ptr.h" +#include "services/shell/public/cpp/connect.h" +#include "services/shell/public/cpp/identity.h" +#include "services/shell/public/cpp/interface_registry.h" +#include "services/shell/public/interfaces/connector.mojom.h" +#include "services/shell/public/interfaces/interface_provider.mojom.h" + +namespace shell { + +class InterfaceBinder; + +// Represents a connection to another application. An instance of this class is +// returned from Shell's ConnectToApplication(), and passed to ShellClient's +// AcceptConnection() each time an incoming connection is received. +// +// Call AddService<T>(factory) to expose an interface to the remote application, +// and GetInterface(&interface_ptr) to consume an interface exposed by the +// remote application. +// +// Internally, this class wraps an InterfaceRegistry that accepts interfaces +// that may be exposed to a remote application. See documentation in +// interface_registry.h for more information. +// +// A Connection returned via Shell::ConnectToApplication() is owned by the +// caller. +// An Connection received via AcceptConnection is owned by the ShellConnection. +// To close a connection, call CloseConnection which will destroy this object. +class Connection { + public: + virtual ~Connection() {} + + enum class State { + // The shell has not yet processed the connection. + PENDING, + + // The shell processed the connection and it was established. GetResult() + // returns mojom::ConnectionResult::SUCCESS. + CONNECTED, + + // The shell processed the connection and establishment was prevented by + // an error, call GetResult(). + DISCONNECTED + }; + + class TestApi { + public: + explicit TestApi(Connection* connection) : connection_(connection) {} + base::WeakPtr<Connection> GetWeakPtr() { + return connection_->GetWeakPtr(); + } + + private: + Connection* connection_; + }; + + // Allow the remote application to request instances of Interface. + // |factory| will create implementations of Interface on demand. + // Returns true if the interface was exposed, false if capability filtering + // from the shell prevented the interface from being exposed. + template <typename Interface> + bool AddInterface(InterfaceFactory<Interface>* factory) { + return GetLocalRegistry()->AddInterface<Interface>(factory); + } + + // Binds |ptr| to an implemention of Interface in the remote application. + // |ptr| can immediately be used to start sending requests to the remote + // interface. + template <typename Interface> + void GetInterface(mojo::InterfacePtr<Interface>* ptr) { + shell::GetInterface(GetRemoteInterfaces(), ptr); + } + + // Returns true if the remote application has the specified capability class + // specified in its manifest. Only valid for inbound connections. Will return + // false for outbound connections. + virtual bool HasCapabilityClass(const std::string& class_name) const = 0; + + // Returns the name that was used by the source application to establish a + // connection to the destination application. + // + // When Connection is representing and outgoing connection, this will be the + // same as the value returned by GetRemoveApplicationName(). + virtual const std::string& GetConnectionName() = 0; + + // Returns the remote identity. While the connection is in the pending state, + // the user_id() field will be the value passed via Connect(). After the + // connection is completed, it will change to the value assigned by the shell. + // Call AddConnectionCompletedClosure() to schedule a closure to be run when + // the resolved user id is available. + virtual const Identity& GetRemoteIdentity() const = 0; + + // Register a handler to receive an error notification on the pipe to the + // remote application's InterfaceProvider. + virtual void SetConnectionLostClosure(const mojo::Closure& handler) = 0; + + // Returns the result of the connection. This function should only be called + // when the connection state is not pending. Call + // AddConnectionCompletedClosure() to schedule a closure to be run when the + // connection is processed by the shell. + virtual mojom::ConnectResult GetResult() const = 0; + + // Returns true if the connection has not yet been processed by the shell. + virtual bool IsPending() const = 0; + + // Returns the instance id of the remote application if it is known at the + // time this function is called. When IsPending() returns true, this function + // will return mojom::kInvalidInstanceID. Use + // AddConnectionCompletedClosure() to schedule a closure to be run when the + // connection is processed by the shell and remote id is available. + virtual uint32_t GetRemoteInstanceID() const = 0; + + // Register a closure to be run when the connection has been completed by the + // shell and remote metadata is available. Useful only for connections created + // via Connector::Connect(). Once the connection is complete, metadata is + // available immediately. + virtual void AddConnectionCompletedClosure(const mojo::Closure& callback) = 0; + + // Returns true if the Shell allows |interface_name| to be exposed to the + // remote application. + virtual bool AllowsInterface(const std::string& interface_name) const = 0; + + // Returns the raw proxy to the remote application's InterfaceProvider + // interface. Most applications will just use GetInterface() instead. + // Caller does not take ownership. + virtual mojom::InterfaceProvider* GetRemoteInterfaces() = 0; + + protected: + virtual InterfaceRegistry* GetLocalRegistry() = 0; + + virtual base::WeakPtr<Connection> GetWeakPtr() = 0; +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_CONNECTION_H_ diff --git a/chromium/services/shell/public/cpp/connector.h b/chromium/services/shell/public/cpp/connector.h new file mode 100644 index 00000000000..6e40590b9a3 --- /dev/null +++ b/chromium/services/shell/public/cpp/connector.h @@ -0,0 +1,104 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_CONNECTOR_H_ +#define SERVICES_SHELL_PUBLIC_CPP_CONNECTOR_H_ + +#include <memory> + +#include "services/shell/public/cpp/connection.h" +#include "services/shell/public/cpp/identity.h" +#include "services/shell/public/interfaces/connector.mojom.h" +#include "services/shell/public/interfaces/shell.mojom.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" + +namespace shell { + +// An interface that encapsulates the Mojo Shell's broker interface by which +// connections between applications are established. Once Connect() is called, +// this class is bound to the thread the call was made on and it cannot be +// passed to another thread without calling Clone(). +// An instance of this class is created internally by ShellConnection for use +// on the thread ShellConnection is instantiated on, and this interface is +// wrapped by the Shell interface. +// To use this interface on other threads, call Shell::CloneConnector() and +// pass the result to another thread. To pass to subsequent threads, call +// Clone() on instances of this object. +// While instances of this object are owned by the caller, the underlying +// connection with the shell is bound to the lifetime of the instance that +// created it, i.e. when the application is terminated the Connector pipe is +// closed. +class Connector { + public: + virtual ~Connector() {} + + class ConnectParams { + public: + explicit ConnectParams(const Identity& target); + explicit ConnectParams(const std::string& name); + ~ConnectParams(); + + const Identity& target() { return target_; } + void set_target(const Identity& target) { target_ = target; } + void set_client_process_connection( + mojom::ShellClientPtr shell_client, + mojom::PIDReceiverRequest pid_receiver_request) { + shell_client_ = std::move(shell_client); + pid_receiver_request_ = std::move(pid_receiver_request); + } + void TakeClientProcessConnection( + mojom::ShellClientPtr* shell_client, + mojom::PIDReceiverRequest* pid_receiver_request) { + *shell_client = std::move(shell_client_); + *pid_receiver_request = std::move(pid_receiver_request_); + } + + private: + Identity target_; + mojom::ShellClientPtr shell_client_; + mojom::PIDReceiverRequest pid_receiver_request_; + + DISALLOW_COPY_AND_ASSIGN(ConnectParams); + }; + + // Requests a new connection to an application. Returns a pointer to the + // connection if the connection is permitted by this application's delegate, + // or nullptr otherwise. Caller takes ownership. + // Once this method is called, this object is bound to the thread on which the + // call took place. To pass to another thread, call Clone() and pass the + // result. + virtual std::unique_ptr<Connection> Connect(const std::string& name) = 0; + virtual std::unique_ptr<Connection> Connect(ConnectParams* params) = 0; + + // Connect to application identified by |request->name| and connect to the + // service implementation of the interface identified by |Interface|. + template <typename Interface> + void ConnectToInterface(ConnectParams* params, + mojo::InterfacePtr<Interface>* ptr) { + std::unique_ptr<Connection> connection = Connect(params); + if (connection) + connection->GetInterface(ptr); + } + template <typename Interface> + void ConnectToInterface(const Identity& target, + mojo::InterfacePtr<Interface>* ptr) { + ConnectParams params(target); + return ConnectToInterface(¶ms, ptr); + } + template <typename Interface> + void ConnectToInterface(const std::string& name, + mojo::InterfacePtr<Interface>* ptr) { + ConnectParams params(name); + return ConnectToInterface(¶ms, ptr); + } + + // Creates a new instance of this class which may be passed to another thread. + // The returned object may be passed multiple times until Connect() is called, + // at which point this method must be called again to pass again. + virtual std::unique_ptr<Connector> Clone() = 0; +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_CONNECTOR_H_ diff --git a/chromium/services/shell/public/cpp/identity.h b/chromium/services/shell/public/cpp/identity.h new file mode 100644 index 00000000000..3c2cb0b1cb5 --- /dev/null +++ b/chromium/services/shell/public/cpp/identity.h @@ -0,0 +1,60 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_IDENTITY_H_ +#define SERVICES_SHELL_PUBLIC_CPP_IDENTITY_H_ + +#include <string> + +#include "services/shell/public/interfaces/connector.mojom.h" + +namespace shell { + +// Represents the identity of an application. +// |name| is the structured name of the application. +// |instance| is a string that allows to tie a specific instance to another. A +// typical use case of instance is to control process grouping for a given name. +class Identity { + public: + Identity(); + Identity(const std::string& name, + const std::string& user_id); + Identity(const std::string& name, + const std::string& user_id, + const std::string& instance); + Identity(const Identity& other); + ~Identity(); + + bool operator<(const Identity& other) const; + bool is_null() const { return name_.empty(); } + bool operator==(const Identity& other) const; + + const std::string& name() const { return name_; } + const std::string& user_id() const { return user_id_; } + void set_user_id(const std::string& user_id) { user_id_ = user_id; } + const std::string& instance() const { return instance_; } + + private: + std::string name_; + std::string user_id_; + std::string instance_; +}; + +} // namespace shell + +namespace mojo { + +template <> +struct TypeConverter<shell::mojom::IdentityPtr, shell::Identity> { + static shell::mojom::IdentityPtr Convert(const shell::Identity& input); +}; + +template <> +struct TypeConverter<shell::Identity, shell::mojom::IdentityPtr> { + static shell::Identity Convert(const shell::mojom::IdentityPtr& input); +}; + +} // namespace mojo + +#endif // SERVICES_SHELL_PUBLIC_CPP_IDENTITY_H_ diff --git a/chromium/services/shell/public/cpp/interface_binder.h b/chromium/services/shell/public/cpp/interface_binder.h new file mode 100644 index 00000000000..9dec4addde6 --- /dev/null +++ b/chromium/services/shell/public/cpp/interface_binder.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_INTERFACE_BINDER_H_ +#define SERVICES_SHELL_PUBLIC_CPP_INTERFACE_BINDER_H_ + +#include <string> + +#include "mojo/public/cpp/system/message_pipe.h" + +namespace shell { + +class Connection; + +class InterfaceBinder { + public: + virtual ~InterfaceBinder() {} + + // Asks the InterfaceBinder to bind an implementation of the specified + // interface to the request passed via |handle|. If the InterfaceBinder binds + // an implementation it must take ownership of the request handle. + virtual void BindInterface(Connection* connection, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle handle) = 0; +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_INTERFACE_BINDER_H_ diff --git a/chromium/services/shell/public/cpp/interface_factory.h b/chromium/services/shell/public/cpp/interface_factory.h new file mode 100644 index 00000000000..165645d61b6 --- /dev/null +++ b/chromium/services/shell/public/cpp/interface_factory.h @@ -0,0 +1,28 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_H_ +#define SERVICES_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_H_ + +#include "mojo/public/cpp/bindings/interface_request.h" + +namespace shell { + +class Connection; + +// Implement this class to provide implementations of a given interface and +// bind them to incoming requests. The implementation of this class is +// responsible for managing the lifetime of the implementations of the +// interface. +template <typename Interface> +class InterfaceFactory { + public: + virtual ~InterfaceFactory() {} + virtual void Create(Connection* connection, + mojo::InterfaceRequest<Interface> request) = 0; +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_H_ diff --git a/chromium/services/shell/public/cpp/interface_factory_impl.h b/chromium/services/shell/public/cpp/interface_factory_impl.h new file mode 100644 index 00000000000..7510a0843b9 --- /dev/null +++ b/chromium/services/shell/public/cpp/interface_factory_impl.h @@ -0,0 +1,49 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_IMPL_H_ +#define SERVICES_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_IMPL_H_ + +#include "services/shell/public/cpp/interface_factory.h" + +namespace shell { + +// Use this class to allocate and bind instances of Impl to interface requests. +// The lifetime of the constructed Impl is bound to the pipe. +template <typename Impl, + typename Interface = typename Impl::ImplementedInterface> +class InterfaceFactoryImpl : public InterfaceFactory<Interface> { + public: + virtual ~InterfaceFactoryImpl() {} + + virtual void Create(Connection* connection, + mojo::InterfaceRequest<Interface> request) override { + BindToRequest(new Impl(), &request); + } +}; + +// Use this class to allocate and bind instances of Impl constructed with a +// context parameter to interface requests. The lifetime of the constructed +// Impl is bound to the pipe. +template <typename Impl, + typename Context, + typename Interface = typename Impl::ImplementedInterface> +class InterfaceFactoryImplWithContext : public InterfaceFactory<Interface> { + public: + explicit InterfaceFactoryImplWithContext(Context* context) + : context_(context) {} + virtual ~InterfaceFactoryImplWithContext() {} + + virtual void Create(Connection* connection, + mojo::InterfaceRequest<Interface> request) override { + BindToRequest(new Impl(context_), &request); + } + + private: + Context* context_; +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_IMPL_H_ diff --git a/chromium/services/shell/public/cpp/interface_registry.h b/chromium/services/shell/public/cpp/interface_registry.h new file mode 100644 index 00000000000..89f8df76357 --- /dev/null +++ b/chromium/services/shell/public/cpp/interface_registry.h @@ -0,0 +1,112 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_INTERFACE_REGISTRY_H_ +#define SERVICES_SHELL_PUBLIC_CPP_INTERFACE_REGISTRY_H_ + +#include <memory> + +#include "base/memory/ptr_util.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "services/shell/public/cpp/lib/interface_factory_binder.h" +#include "services/shell/public/interfaces/interface_provider.mojom.h" + +namespace shell { +class InterfaceBinder; + +// An implementation of mojom::InterfaceProvider that allows the user to +// register services to be exposed to another application. +// +// To use, define a class that implements your specific interface. Then +// implement an InterfaceFactory<Foo> that binds instances of FooImpl to +// InterfaceRequest<Foo>s and register that on the registry like this: +// +// registry.AddInterface(&factory); +// +// Or, if you have multiple factories implemented by the same type, explicitly +// specify the interface to register the factory for: +// +// registry.AddInterface<Foo>(&my_foo_and_bar_factory_); +// registry.AddInterface<Bar>(&my_foo_and_bar_factory_); +// +// The InterfaceFactory must outlive the InterfaceRegistry. +// +// Additionally you may specify a default InterfaceBinder to handle requests for +// interfaces unhandled by any registered InterfaceFactory. Just as with +// InterfaceFactory, the default InterfaceBinder supplied must outlive +// InterfaceRegistry. +// +class InterfaceRegistry : public mojom::InterfaceProvider { + public: + class TestApi { + public: + explicit TestApi(InterfaceRegistry* registry) : registry_(registry) {} + ~TestApi() {} + + void SetInterfaceBinderForName(InterfaceBinder* binder, + const std::string& interface_name) { + registry_->SetInterfaceBinderForName( + base::WrapUnique(binder), interface_name); + } + + void RemoveInterfaceBinderForName(const std::string& interface_name) { + registry_->RemoveInterfaceBinderForName(interface_name); + } + + private: + InterfaceRegistry* registry_; + DISALLOW_COPY_AND_ASSIGN(TestApi); + }; + + // Construct with a Connection (which may be null), and create an + // InterfaceProvider pipe, the client end of which may be obtained by calling + // TakeClientHandle(). If |connection| is non-null, the Mojo Shell's + // rules filtering which interfaces are allowed to be exposed to clients are + // imposed on this registry. If null, they are not. + explicit InterfaceRegistry(Connection* connection); + // Construct with an InterfaceProviderRequest and a Connection (which may be + // null, see note above about filtering). + InterfaceRegistry(mojom::InterfaceProviderRequest request, + Connection* connection); + ~InterfaceRegistry() override; + + // Takes the client end of the InterfaceProvider pipe created in the + // constructor. + mojom::InterfaceProviderPtr TakeClientHandle(); + + template <typename Interface> + bool AddInterface(InterfaceFactory<Interface>* factory) { + return SetInterfaceBinderForName( + base::WrapUnique( + new internal::InterfaceFactoryBinder<Interface>(factory)), + Interface::Name_); + } + + private: + using NameToInterfaceBinderMap = + std::map<std::string, std::unique_ptr<InterfaceBinder>>; + + // mojom::InterfaceProvider: + void GetInterface(const mojo::String& interface_name, + mojo::ScopedMessagePipeHandle handle) override; + + // Returns true if the binder was set, false if it was not set (e.g. by + // some filtering policy preventing this interface from being exposed). + bool SetInterfaceBinderForName(std::unique_ptr<InterfaceBinder> binder, + const std::string& name); + + void RemoveInterfaceBinderForName(const std::string& interface_name); + + mojom::InterfaceProviderPtr client_handle_; + mojo::Binding<mojom::InterfaceProvider> binding_; + Connection* connection_; + + NameToInterfaceBinderMap name_to_binder_; + + DISALLOW_COPY_AND_ASSIGN(InterfaceRegistry); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_INTERFACE_REGISTRY_H_ diff --git a/chromium/services/shell/public/cpp/lib/application_runner.cc b/chromium/services/shell/public/cpp/lib/application_runner.cc new file mode 100644 index 00000000000..2b5a0f2e627 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/application_runner.cc @@ -0,0 +1,91 @@ +// Copyright 2014 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 "services/shell/public/cpp/application_runner.h" + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/process/launch.h" +#include "base/run_loop.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/cpp/shell_connection.h" + +namespace shell { + +int g_application_runner_argc; +const char* const* g_application_runner_argv; + +ApplicationRunner::ApplicationRunner(ShellClient* client) + : client_(std::unique_ptr<ShellClient>(client)), + message_loop_type_(base::MessageLoop::TYPE_DEFAULT), + has_run_(false) {} + +ApplicationRunner::~ApplicationRunner() {} + +void ApplicationRunner::InitBaseCommandLine() { + base::CommandLine::Init(g_application_runner_argc, g_application_runner_argv); +} + +void ApplicationRunner::set_message_loop_type(base::MessageLoop::Type type) { + DCHECK_NE(base::MessageLoop::TYPE_CUSTOM, type); + DCHECK(!has_run_); + + message_loop_type_ = type; +} + +MojoResult ApplicationRunner::Run(MojoHandle shell_client_request_handle, + bool init_base) { + DCHECK(!has_run_); + has_run_ = true; + + std::unique_ptr<base::AtExitManager> at_exit; + if (init_base) { + InitBaseCommandLine(); + at_exit.reset(new base::AtExitManager); + } + + { + std::unique_ptr<base::MessageLoop> loop; + loop.reset(new base::MessageLoop(message_loop_type_)); + + connection_.reset(new ShellConnection( + client_.get(), + mojo::MakeRequest<mojom::ShellClient>(mojo::MakeScopedHandle( + mojo::MessagePipeHandle(shell_client_request_handle))))); + base::RunLoop run_loop; + connection_->SetConnectionLostClosure(run_loop.QuitClosure()); + run_loop.Run(); + // It's very common for the client to cache the app and terminate on errors. + // If we don't delete the client before the app we run the risk of the + // client having a stale reference to the app and trying to use it. + // Note that we destruct the message loop first because that might trigger + // connection error handlers and they might access objects created by the + // client. + loop.reset(); + client_.reset(); + connection_.reset(); + } + return MOJO_RESULT_OK; +} + +MojoResult ApplicationRunner::Run(MojoHandle shell_client_request_handle) { + bool init_base = true; + if (base::CommandLine::InitializedForCurrentProcess()) { + init_base = + !base::CommandLine::ForCurrentProcess()->HasSwitch("single-process"); + } + return Run(shell_client_request_handle, init_base); +} + +void ApplicationRunner::DestroyShellConnection() { + connection_.reset(); +} + +void ApplicationRunner::Quit() { + base::MessageLoop::current()->QuitWhenIdle(); +} + +} // namespace shell diff --git a/chromium/services/shell/public/cpp/lib/capabilities.cc b/chromium/services/shell/public/cpp/lib/capabilities.cc new file mode 100644 index 00000000000..51dcd0dbfee --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/capabilities.cc @@ -0,0 +1,85 @@ +// 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 "services/shell/public/cpp/capabilities.h" + +namespace shell { + +CapabilityRequest::CapabilityRequest() {} +CapabilityRequest::CapabilityRequest(const CapabilityRequest& other) = default; +CapabilityRequest::~CapabilityRequest() {} + +bool CapabilityRequest::operator==(const CapabilityRequest& other) const { + return other.classes == classes && other.interfaces == interfaces; +} + +bool CapabilityRequest::operator<(const CapabilityRequest& other) const { + return std::tie(classes, interfaces) < + std::tie(other.classes, other.interfaces); +} + +CapabilitySpec::CapabilitySpec() {} +CapabilitySpec::CapabilitySpec(const CapabilitySpec& other) = default; +CapabilitySpec::~CapabilitySpec() {} + +bool CapabilitySpec::operator==(const CapabilitySpec& other) const { + return other.provided == provided && other.required == required; +} + +bool CapabilitySpec::operator<(const CapabilitySpec& other) const { + return std::tie(provided, required) < + std::tie(other.provided, other.required); +} + +} // namespace shell + +namespace mojo { + +// static +shell::mojom::CapabilitySpecPtr +TypeConverter<shell::mojom::CapabilitySpecPtr, shell::CapabilitySpec>::Convert( + const shell::CapabilitySpec& input) { + shell::mojom::CapabilitySpecPtr spec(shell::mojom::CapabilitySpec::New()); + spec->provided = + mojo::Map<mojo::String, mojo::Array<mojo::String>>::From(input.provided); + spec->required = + mojo::Map<mojo::String, shell::mojom::CapabilityRequestPtr>::From( + input.required); + return spec; +} + +// static +shell::CapabilitySpec +TypeConverter<shell::CapabilitySpec, shell::mojom::CapabilitySpecPtr>::Convert( + const shell::mojom::CapabilitySpecPtr& input) { + shell::CapabilitySpec spec; + spec.provided = + input->provided.To<std::map<shell::Class, shell::Interfaces>>(); + spec.required = + input->required.To<std::map<shell::Name, shell::CapabilityRequest>>(); + return spec; +} + +// static +shell::mojom::CapabilityRequestPtr TypeConverter< + shell::mojom::CapabilityRequestPtr, + shell::CapabilityRequest>::Convert(const shell::CapabilityRequest& input) { + shell::mojom::CapabilityRequestPtr request( + shell::mojom::CapabilityRequest::New()); + request->classes = mojo::Array<mojo::String>::From(input.classes); + request->interfaces = mojo::Array<mojo::String>::From(input.interfaces); + return request; +} + +// static +shell::CapabilityRequest +TypeConverter<shell::CapabilityRequest, shell::mojom::CapabilityRequestPtr>:: + Convert(const shell::mojom::CapabilityRequestPtr& input) { + shell::CapabilityRequest request; + request.classes = input->classes.To<std::set<std::string>>(); + request.interfaces = input->interfaces.To<std::set<std::string>>(); + return request; +} + +} // namespace mojo diff --git a/chromium/services/shell/public/cpp/lib/connection_impl.cc b/chromium/services/shell/public/cpp/lib/connection_impl.cc new file mode 100644 index 00000000000..60fd9888f47 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/connection_impl.cc @@ -0,0 +1,129 @@ +// Copyright 2014 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 "services/shell/public/cpp/lib/connection_impl.h" + +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "services/shell/public/cpp/connection.h" +#include "services/shell/public/cpp/interface_binder.h" + +namespace shell { +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// ConnectionImpl, public: + +ConnectionImpl::ConnectionImpl( + const std::string& connection_name, + const Identity& remote, + uint32_t remote_id, + shell::mojom::InterfaceProviderPtr remote_interfaces, + shell::mojom::InterfaceProviderRequest local_interfaces, + const CapabilityRequest& capability_request, + State initial_state) + : connection_name_(connection_name), + remote_(remote), + remote_id_(remote_id), + state_(initial_state), + local_registry_(std::move(local_interfaces), this), + remote_interfaces_(std::move(remote_interfaces)), + capability_request_(capability_request), + allow_all_interfaces_(capability_request.interfaces.size() == 1 && + capability_request.interfaces.count("*") == 1), + weak_factory_(this) {} + +ConnectionImpl::ConnectionImpl() + : local_registry_(shell::mojom::InterfaceProviderRequest(), this), + allow_all_interfaces_(true), + weak_factory_(this) {} + +ConnectionImpl::~ConnectionImpl() {} + +shell::mojom::Connector::ConnectCallback ConnectionImpl::GetConnectCallback() { + return base::Bind(&ConnectionImpl::OnConnectionCompleted, + weak_factory_.GetWeakPtr()); +} + +//////////////////////////////////////////////////////////////////////////////// +// ConnectionImpl, Connection implementation: + +bool ConnectionImpl::HasCapabilityClass(const std::string& class_name) const { + return capability_request_.classes.count(class_name) > 0; +} + +const std::string& ConnectionImpl::GetConnectionName() { + return connection_name_; +} + +const Identity& ConnectionImpl::GetRemoteIdentity() const { + return remote_; +} + +void ConnectionImpl::SetConnectionLostClosure(const mojo::Closure& handler) { + remote_interfaces_.set_connection_error_handler(handler); +} + +shell::mojom::ConnectResult ConnectionImpl::GetResult() const { + return result_; +} + +bool ConnectionImpl::IsPending() const { + return state_ == State::PENDING; +} + +uint32_t ConnectionImpl::GetRemoteInstanceID() const { + return remote_id_; +} + +void ConnectionImpl::AddConnectionCompletedClosure( + const mojo::Closure& callback) { + if (IsPending()) + connection_completed_callbacks_.push_back(callback); + else + callback.Run(); +} + +bool ConnectionImpl::AllowsInterface(const std::string& interface_name) const { + return allow_all_interfaces_ || + capability_request_.interfaces.count(interface_name); +} + +shell::mojom::InterfaceProvider* ConnectionImpl::GetRemoteInterfaces() { + return remote_interfaces_.get(); +} + +InterfaceRegistry* ConnectionImpl::GetLocalRegistry() { + return &local_registry_; +} + +base::WeakPtr<Connection> ConnectionImpl::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +//////////////////////////////////////////////////////////////////////////////// +// ConnectionImpl, private: + +void ConnectionImpl::OnConnectionCompleted(shell::mojom::ConnectResult result, + const std::string& target_user_id, + uint32_t target_application_id) { + DCHECK(State::PENDING == state_); + + result_ = result; + state_ = result_ == shell::mojom::ConnectResult::SUCCEEDED ? + State::CONNECTED : State::DISCONNECTED; + remote_id_ = target_application_id; + remote_.set_user_id(target_user_id); + std::vector<mojo::Closure> callbacks; + callbacks.swap(connection_completed_callbacks_); + for (auto callback : callbacks) + callback.Run(); +} + +} // namespace internal +} // namespace shell diff --git a/chromium/services/shell/public/cpp/lib/connection_impl.h b/chromium/services/shell/public/cpp/lib/connection_impl.h new file mode 100644 index 00000000000..246cdb2aba1 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/connection_impl.h @@ -0,0 +1,84 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_LIB_CONNECTION_IMPL_H_ +#define SERVICES_SHELL_PUBLIC_CPP_LIB_CONNECTION_IMPL_H_ + +#include <stdint.h> + +#include <set> +#include <string> + +#include "mojo/public/cpp/bindings/binding.h" +#include "services/shell/public/cpp/capabilities.h" +#include "services/shell/public/cpp/connection.h" +#include "services/shell/public/cpp/identity.h" +#include "services/shell/public/interfaces/connector.mojom.h" +#include "services/shell/public/interfaces/interface_provider.mojom.h" + +namespace shell { +namespace internal { + +// A ConnectionImpl represents each half of a connection between two +// applications, allowing customization of which interfaces are published to the +// other. +class ConnectionImpl : public Connection { + public: + ConnectionImpl(); + // |allowed_interfaces| are the set of interfaces that the shell has allowed + // an application to expose to another application. If this set contains only + // the string value "*" all interfaces may be exposed. + ConnectionImpl(const std::string& connection_name, + const Identity& remote, + uint32_t remote_id, + shell::mojom::InterfaceProviderPtr remote_interfaces, + shell::mojom::InterfaceProviderRequest local_interfaces, + const CapabilityRequest& capability_request, + State initial_state); + ~ConnectionImpl() override; + + shell::mojom::Connector::ConnectCallback GetConnectCallback(); + + private: + // Connection: + bool HasCapabilityClass(const std::string& class_name) const override; + const std::string& GetConnectionName() override; + const Identity& GetRemoteIdentity() const override; + void SetConnectionLostClosure(const mojo::Closure& handler) override; + shell::mojom::ConnectResult GetResult() const override; + bool IsPending() const override; + uint32_t GetRemoteInstanceID() const override; + void AddConnectionCompletedClosure(const mojo::Closure& callback) override; + bool AllowsInterface(const std::string& interface_name) const override; + shell::mojom::InterfaceProvider* GetRemoteInterfaces() override; + InterfaceRegistry* GetLocalRegistry() override; + base::WeakPtr<Connection> GetWeakPtr() override; + + void OnConnectionCompleted(shell::mojom::ConnectResult result, + const std::string& target_user_id, + uint32_t target_application_id); + + const std::string connection_name_; + Identity remote_; + uint32_t remote_id_ = shell::mojom::kInvalidInstanceID; + + State state_; + shell::mojom::ConnectResult result_ = shell::mojom::ConnectResult::SUCCEEDED; + std::vector<mojo::Closure> connection_completed_callbacks_; + + InterfaceRegistry local_registry_; + shell::mojom::InterfaceProviderPtr remote_interfaces_; + + const CapabilityRequest capability_request_; + const bool allow_all_interfaces_; + + base::WeakPtrFactory<ConnectionImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ConnectionImpl); +}; + +} // namespace internal +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_LIB_CONNECTION_IMPL_H_ diff --git a/chromium/services/shell/public/cpp/lib/connector_impl.cc b/chromium/services/shell/public/cpp/lib/connector_impl.cc new file mode 100644 index 00000000000..1bd9e68924c --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/connector_impl.cc @@ -0,0 +1,94 @@ +// 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 "services/shell/public/cpp/lib/connector_impl.h" + +#include "base/memory/ptr_util.h" +#include "services/shell/public/cpp/identity.h" +#include "services/shell/public/cpp/lib/connection_impl.h" + +namespace shell { + +Connector::ConnectParams::ConnectParams(const Identity& target) + : target_(target) {} + +Connector::ConnectParams::ConnectParams(const std::string& name) + : target_(name, mojom::kInheritUserID) {} + +Connector::ConnectParams::~ConnectParams() {} + +ConnectorImpl::ConnectorImpl(mojom::ConnectorPtrInfo unbound_state) + : unbound_state_(std::move(unbound_state)) {} + +ConnectorImpl::ConnectorImpl(mojom::ConnectorPtr connector) + : connector_(std::move(connector)) { + thread_checker_.reset(new base::ThreadChecker); +} + +ConnectorImpl::~ConnectorImpl() {} + +std::unique_ptr<Connection> ConnectorImpl::Connect(const std::string& name) { + ConnectParams params(name); + return Connect(¶ms); +} + +std::unique_ptr<Connection> ConnectorImpl::Connect(ConnectParams* params) { + // Bind this object to the current thread the first time it is used to + // connect. + if (!connector_.is_bound()) { + if (!unbound_state_.is_valid()) { + // It's possible to get here when the link to the shell has been severed + // (and so the connector pipe has been closed) but the app has chosen not + // to quit. + return nullptr; + } + connector_.Bind(std::move(unbound_state_)); + thread_checker_.reset(new base::ThreadChecker); + } + DCHECK(thread_checker_->CalledOnValidThread()); + + DCHECK(params); + // We allow all interfaces on outgoing connections since we are presumably in + // a position to know who we're talking to. + CapabilityRequest request; + request.interfaces.insert("*"); + mojom::InterfaceProviderPtr local_interfaces; + mojom::InterfaceProviderRequest local_request = GetProxy(&local_interfaces); + mojom::InterfaceProviderPtr remote_interfaces; + mojom::InterfaceProviderRequest remote_request = GetProxy(&remote_interfaces); + std::unique_ptr<internal::ConnectionImpl> registry( + new internal::ConnectionImpl( + params->target().name(), params->target(), mojom::kInvalidInstanceID, + std::move(remote_interfaces), std::move(local_request), request, + Connection::State::PENDING)); + + mojom::ShellClientPtr shell_client; + mojom::PIDReceiverRequest pid_receiver_request; + params->TakeClientProcessConnection(&shell_client, &pid_receiver_request); + mojom::ClientProcessConnectionPtr client_process_connection; + if (shell_client.is_bound() && pid_receiver_request.is_pending()) { + client_process_connection = mojom::ClientProcessConnection::New(); + client_process_connection->shell_client = + shell_client.PassInterface().PassHandle(); + client_process_connection->pid_receiver_request = + pid_receiver_request.PassMessagePipe(); + } else if (shell_client.is_bound() || pid_receiver_request.is_pending()) { + NOTREACHED() << "If one of shell_client or pid_receiver_request is valid, " + << "both must be valid."; + return std::move(registry); + } + connector_->Connect(mojom::Identity::From(params->target()), + std::move(remote_request), std::move(local_interfaces), + std::move(client_process_connection), + registry->GetConnectCallback()); + return std::move(registry); +} + +std::unique_ptr<Connector> ConnectorImpl::Clone() { + mojom::ConnectorPtr connector; + connector_->Clone(GetProxy(&connector)); + return base::WrapUnique(new ConnectorImpl(connector.PassInterface())); +} + +} // namespace shell diff --git a/chromium/services/shell/public/cpp/lib/connector_impl.h b/chromium/services/shell/public/cpp/lib/connector_impl.h new file mode 100644 index 00000000000..128469a7a0f --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/connector_impl.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_LIB_CONNECTOR_IMPL_H_ +#define SERVICES_SHELL_PUBLIC_CPP_LIB_CONNECTOR_IMPL_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/threading/thread_checker.h" +#include "services/shell/public/cpp/connector.h" +#include "services/shell/public/interfaces/connector.mojom.h" + +namespace shell { + +class ConnectorImpl : public Connector { + public: + explicit ConnectorImpl(mojom::ConnectorPtrInfo unbound_state); + explicit ConnectorImpl(mojom::ConnectorPtr connector); + ~ConnectorImpl() override; + + private: + // Connector: + std::unique_ptr<Connection> Connect(const std::string& name) override; + std::unique_ptr<Connection> Connect(ConnectParams* params) override; + std::unique_ptr<Connector> Clone() override; + + mojom::ConnectorPtrInfo unbound_state_; + mojom::ConnectorPtr connector_; + + std::unique_ptr<base::ThreadChecker> thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(ConnectorImpl); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_LIB_CONNECTOR_IMPL_H_ diff --git a/chromium/services/shell/public/cpp/lib/identity.cc b/chromium/services/shell/public/cpp/lib/identity.cc new file mode 100644 index 00000000000..dc49424fbf0 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/identity.cc @@ -0,0 +1,65 @@ +// 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 "services/shell/public/cpp/identity.h" + +#include "base/guid.h" +#include "services/shell/public/cpp/names.h" + +namespace shell { + +Identity::Identity() {} + +Identity::Identity(const std::string& name, const std::string& user_id) + : Identity(name, user_id, "") {} + +Identity::Identity(const std::string& name, const std::string& user_id, + const std::string& instance) + : name_(name), + user_id_(user_id), + instance_(instance.empty() ? GetNamePath(name_) : instance) { + CHECK(!user_id.empty()); + CHECK(base::IsValidGUID(user_id)); +} + +Identity::Identity(const Identity& other) = default; + +Identity::~Identity() {} + +bool Identity::operator<(const Identity& other) const { + if (name_ != other.name_) + return name_ < other.name_; + if (instance_ != other.instance_) + return instance_ < other.instance_; + return user_id_ < other.user_id_; +} + +bool Identity::operator==(const Identity& other) const { + return other.name_ == name_ && other.instance_ == instance_ && + other.user_id_ == user_id_; +} + +} // namespace shell + +namespace mojo { + +// static +shell::mojom::IdentityPtr +TypeConverter<shell::mojom::IdentityPtr, shell::Identity>::Convert( + const shell::Identity& input) { + shell::mojom::IdentityPtr identity(shell::mojom::Identity::New()); + identity->name = input.name(); + identity->user_id = input.user_id(); + identity->instance = input.instance(); + return identity; +} + +// static +shell::Identity +TypeConverter<shell::Identity, shell::mojom::IdentityPtr>::Convert( + const shell::mojom::IdentityPtr& input) { + return shell::Identity(input->name, input->user_id, input->instance); +} + +} // namespace mojo diff --git a/chromium/services/shell/public/cpp/lib/init_commandline.cc b/chromium/services/shell/public/cpp/lib/init_commandline.cc new file mode 100644 index 00000000000..73c201116f0 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/init_commandline.cc @@ -0,0 +1,22 @@ +// Copyright 2014 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 "build/build_config.h" + +namespace shell { + +extern int g_application_runner_argc; +extern const char* const* g_application_runner_argv; + +} // namespace shell + +#if !defined(OS_WIN) +extern "C" { +__attribute__((visibility("default"))) void InitCommandLineArgs( + int argc, const char* const* argv) { + shell::g_application_runner_argc = argc; + shell::g_application_runner_argv = argv; +} +} +#endif diff --git a/chromium/services/shell/public/cpp/lib/initialize_base_and_icu.cc b/chromium/services/shell/public/cpp/lib/initialize_base_and_icu.cc new file mode 100644 index 00000000000..b910f0feea0 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/initialize_base_and_icu.cc @@ -0,0 +1,54 @@ +// 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. + +// This file declares a raw symbol and should be included only once in a +// certain binary target. This needs to be run before we raise the sandbox, +// which means that it can't use mojo. Our runners will dig around in the +// symbol table and run this before the mojo system is initialized. + +#include <stdint.h> + +#include <memory> + +#include "base/files/file.h" +#include "base/i18n/icu_util.h" +#include "base/rand_util.h" +#include "base/sys_info.h" +#include "mojo/public/c/system/types.h" + +#if !defined(OS_ANDROID) +#include "third_party/icu/source/i18n/unicode/timezone.h" +#endif + +extern "C" { +#if defined(WIN32) +__declspec(dllexport) void __cdecl +#else +void __attribute__((visibility("default"))) +#endif +InitializeBase(const uint8_t* icu_data) { + base::RandUint64(); + base::SysInfo::AmountOfPhysicalMemory(); + base::SysInfo::NumberOfProcessors(); +#if defined(OS_POSIX) && !defined(OS_MACOSX) + base::SysInfo::MaxSharedMemorySize(); +#endif + +#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE + // Initialize core ICU. We must perform the full initialization before we + // initialize icu::TimeZone subsystem because otherwise ICU gets in a state + // where the timezone data is disconnected from the locale data which can + // cause crashes. + CHECK(base::i18n::InitializeICUFromRawMemory(icu_data)); +#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE + +#if !defined(OS_ANDROID) + // ICU DateFormat class (used in base/time_format.cc) needs to get the + // Olson timezone ID by accessing the zoneinfo files on disk. After + // TimeZone::createDefault is called once here, the timezone ID is + // cached and there's no more need to access the file system. + std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault()); +#endif +} +} diff --git a/chromium/services/shell/public/cpp/lib/interface_factory_binder.h b/chromium/services/shell/public/cpp/lib/interface_factory_binder.h new file mode 100644 index 00000000000..bd97a762825 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/interface_factory_binder.h @@ -0,0 +1,39 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_LIB_INTERFACE_FACTORY_BINDER_H_ +#define SERVICES_SHELL_PUBLIC_CPP_LIB_INTERFACE_FACTORY_BINDER_H_ + +#include <utility> + +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/shell/public/cpp/interface_binder.h" +#include "services/shell/public/cpp/interface_factory.h" + +namespace shell { +namespace internal { + +template <typename Interface> +class InterfaceFactoryBinder : public InterfaceBinder { + public: + explicit InterfaceFactoryBinder(InterfaceFactory<Interface>* factory) + : factory_(factory) {} + ~InterfaceFactoryBinder() override {} + + void BindInterface(Connection* connection, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle client_handle) override { + factory_->Create(connection, + mojo::MakeRequest<Interface>(std::move(client_handle))); + } + + private: + InterfaceFactory<Interface>* factory_; + DISALLOW_COPY_AND_ASSIGN(InterfaceFactoryBinder); +}; + +} // namespace internal +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_LIB_INTERFACE_FACTORY_BINDER_H_ diff --git a/chromium/services/shell/public/cpp/lib/interface_registry.cc b/chromium/services/shell/public/cpp/lib/interface_registry.cc new file mode 100644 index 00000000000..fefe3e52e7e --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/interface_registry.cc @@ -0,0 +1,62 @@ +// 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 "services/shell/public/cpp/interface_registry.h" + +#include "services/shell/public/cpp/connection.h" + +namespace shell { + +InterfaceRegistry::InterfaceRegistry(Connection* connection) + : InterfaceRegistry(nullptr, connection) {} + +InterfaceRegistry::InterfaceRegistry(mojom::InterfaceProviderRequest request, + Connection* connection) + : binding_(this), connection_(connection) { + if (!request.is_pending()) + request = GetProxy(&client_handle_); + binding_.Bind(std::move(request)); +} + +InterfaceRegistry::~InterfaceRegistry() {} + +mojom::InterfaceProviderPtr InterfaceRegistry::TakeClientHandle() { + return std::move(client_handle_); +} + +// mojom::InterfaceProvider: +void InterfaceRegistry::GetInterface(const mojo::String& interface_name, + mojo::ScopedMessagePipeHandle handle) { + auto iter = name_to_binder_.find(interface_name); + if (iter != name_to_binder_.end()) { + iter->second->BindInterface(connection_, interface_name, std::move(handle)); + } else if (connection_ && connection_->AllowsInterface(interface_name)) { + LOG(ERROR) << "Connection CapabilityFilter prevented binding to " + << "interface: " << interface_name << " connection_name:" + << connection_->GetConnectionName() << " remote_name:" + << connection_->GetRemoteIdentity().name(); + } +} + +bool InterfaceRegistry::SetInterfaceBinderForName( + std::unique_ptr<InterfaceBinder> binder, + const std::string& interface_name) { + if (!connection_ || + (connection_ && connection_->AllowsInterface(interface_name))) { + RemoveInterfaceBinderForName(interface_name); + name_to_binder_[interface_name] = std::move(binder); + return true; + } + return false; +} + +void InterfaceRegistry::RemoveInterfaceBinderForName( + const std::string& interface_name) { + NameToInterfaceBinderMap::iterator it = name_to_binder_.find(interface_name); + if (it == name_to_binder_.end()) + return; + name_to_binder_.erase(it); +} + +} // namespace shell diff --git a/chromium/services/shell/public/cpp/lib/names.cc b/chromium/services/shell/public/cpp/lib/names.cc new file mode 100644 index 00000000000..49122223e82 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/names.cc @@ -0,0 +1,46 @@ +// 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 "services/shell/public/cpp/names.h" + +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" + +namespace shell { + +const char kNameType_Mojo[] = "mojo"; +const char kNameType_Exe[] = "exe"; + +bool IsValidName(const std::string& name) { + std::vector<std::string> parts = + base::SplitString(name, ":", base::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + if (parts.size() != 2) + return false; + + if (parts.front().empty()) + return false; + + const std::string& path = parts.back(); + return !path.empty() && + !base::StartsWith(path, "//", base::CompareCase::INSENSITIVE_ASCII); +} + +std::string GetNameType(const std::string& name) { + std::vector<std::string> parts = + base::SplitString(name, ":", base::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + DCHECK(2 == parts.size()); + return parts.front(); +} + +std::string GetNamePath(const std::string& name) { + std::vector<std::string> parts = + base::SplitString(name, ":", base::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + DCHECK(2 == parts.size()); + return parts.back(); +} + +} // namespace shell diff --git a/chromium/services/shell/public/cpp/lib/shell_client.cc b/chromium/services/shell/public/cpp/lib/shell_client.cc new file mode 100644 index 00000000000..562d6ee46c4 --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/shell_client.cc @@ -0,0 +1,22 @@ +// Copyright 2014 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 "services/shell/public/cpp/shell_client.h" + +namespace shell { + +ShellClient::ShellClient() {} +ShellClient::~ShellClient() {} + +void ShellClient::Initialize(Connector* connector, const Identity& identity, + uint32_t id) { +} + +bool ShellClient::AcceptConnection(Connection* connection) { + return false; +} + +bool ShellClient::ShellConnectionLost() { return true; } + +} // namespace shell diff --git a/chromium/services/shell/public/cpp/lib/shell_connection.cc b/chromium/services/shell/public/cpp/lib/shell_connection.cc new file mode 100644 index 00000000000..8b1ad3fbb6d --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/shell_connection.cc @@ -0,0 +1,105 @@ +// Copyright 2014 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 "services/shell/public/cpp/shell_connection.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/shell/public/cpp/capabilities.h" +#include "services/shell/public/cpp/connector.h" +#include "services/shell/public/cpp/lib/connection_impl.h" +#include "services/shell/public/cpp/lib/connector_impl.h" +#include "services/shell/public/cpp/shell_client.h" + +namespace shell { + +//////////////////////////////////////////////////////////////////////////////// +// ShellConnection, public: + +ShellConnection::ShellConnection(shell::ShellClient* client, + mojom::ShellClientRequest request) + : client_(client), binding_(this) { + mojom::ConnectorPtr connector; + pending_connector_request_ = GetProxy(&connector); + connector_.reset(new ConnectorImpl(std::move(connector))); + + DCHECK(request.is_pending()); + binding_.Bind(std::move(request)); +} + +ShellConnection::~ShellConnection() {} + +void ShellConnection::set_initialize_handler(const base::Closure& callback) { + initialize_handler_ = callback; +} + +void ShellConnection::SetAppTestConnectorForTesting( + mojom::ConnectorPtr connector) { + pending_connector_request_ = nullptr; + connector_.reset(new ConnectorImpl(std::move(connector))); +} + +void ShellConnection::SetConnectionLostClosure(const base::Closure& closure) { + connection_lost_closure_ = closure; + if (should_run_connection_lost_closure_ && + !connection_lost_closure_.is_null()) + connection_lost_closure_.Run(); +} + +//////////////////////////////////////////////////////////////////////////////// +// ShellConnection, mojom::ShellClient implementation: + +void ShellConnection::Initialize(mojom::IdentityPtr identity, + uint32_t id, + const InitializeCallback& callback) { + identity_ = identity.To<Identity>(); + if (!initialize_handler_.is_null()) + initialize_handler_.Run(); + + callback.Run(std::move(pending_connector_request_)); + + DCHECK(binding_.is_bound()); + binding_.set_connection_error_handler([this] { OnConnectionError(); }); + + client_->Initialize(connector_.get(), identity_, id); +} + +void ShellConnection::AcceptConnection( + mojom::IdentityPtr source, + uint32_t source_id, + mojom::InterfaceProviderRequest local_interfaces, + mojom::InterfaceProviderPtr remote_interfaces, + mojom::CapabilityRequestPtr allowed_capabilities, + const mojo::String& name) { + std::unique_ptr<Connection> registry(new internal::ConnectionImpl( + name, source.To<Identity>(), source_id, std::move(remote_interfaces), + std::move(local_interfaces), allowed_capabilities.To<CapabilityRequest>(), + Connection::State::CONNECTED)); + if (!client_->AcceptConnection(registry.get())) + return; + + // TODO(beng): it appears we never prune this list. We should, when the + // connection's remote service provider pipe breaks. + incoming_connections_.push_back(std::move(registry)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ShellConnection, private: + +void ShellConnection::OnConnectionError() { + // Note that the ShellClient doesn't technically have to quit now, it may live + // on to service existing connections. All existing Connectors however are + // invalid. + should_run_connection_lost_closure_ = client_->ShellConnectionLost(); + if (should_run_connection_lost_closure_ && + !connection_lost_closure_.is_null()) + connection_lost_closure_.Run(); + // We don't reset the connector as clients may have taken a raw pointer to it. + // Connect() will return nullptr if they try to connect to anything. +} + +} // namespace shell diff --git a/chromium/services/shell/public/cpp/lib/shell_connection_ref.cc b/chromium/services/shell/public/cpp/lib/shell_connection_ref.cc new file mode 100644 index 00000000000..8b06bb79a4e --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/shell_connection_ref.cc @@ -0,0 +1,87 @@ +// 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 "services/shell/public/cpp/shell_connection_ref.h" + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread_checker.h" +#include "base/threading/thread_task_runner_handle.h" + +namespace shell { + +class ShellConnectionRefImpl : public ShellConnectionRef { + public: + ShellConnectionRefImpl( + base::WeakPtr<ShellConnectionRefFactory> factory, + scoped_refptr<base::SingleThreadTaskRunner> shell_client_task_runner) + : factory_(factory), + shell_client_task_runner_(shell_client_task_runner) { + // This object is not thread-safe but may be used exclusively on a different + // thread from the one which constructed it. + thread_checker_.DetachFromThread(); + } + + ~ShellConnectionRefImpl() override { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (shell_client_task_runner_->BelongsToCurrentThread() && factory_) { + factory_->Release(); + } else { + shell_client_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ShellConnectionRefFactory::Release, factory_)); + } + } + + private: + // ShellConnectionRef: + std::unique_ptr<ShellConnectionRef> Clone() override { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (shell_client_task_runner_->BelongsToCurrentThread() && factory_) { + factory_->AddRef(); + } else { + shell_client_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ShellConnectionRefFactory::AddRef, factory_)); + } + + return base::WrapUnique( + new ShellConnectionRefImpl(factory_, shell_client_task_runner_)); + } + + base::WeakPtr<ShellConnectionRefFactory> factory_; + scoped_refptr<base::SingleThreadTaskRunner> shell_client_task_runner_; + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(ShellConnectionRefImpl); +}; + +ShellConnectionRefFactory::ShellConnectionRefFactory( + const base::Closure& quit_closure) + : quit_closure_(quit_closure), weak_factory_(this) { + DCHECK(!quit_closure_.is_null()); +} + +ShellConnectionRefFactory::~ShellConnectionRefFactory() {} + +std::unique_ptr<ShellConnectionRef> ShellConnectionRefFactory::CreateRef() { + AddRef(); + return base::WrapUnique( + new ShellConnectionRefImpl(weak_factory_.GetWeakPtr(), + base::ThreadTaskRunnerHandle::Get())); +} + +void ShellConnectionRefFactory::AddRef() { + ++ref_count_; +} + +void ShellConnectionRefFactory::Release() { + if (!--ref_count_) + quit_closure_.Run(); +} + +} // namespace shell diff --git a/chromium/services/shell/public/cpp/lib/shell_test.cc b/chromium/services/shell/public/cpp/lib/shell_test.cc new file mode 100644 index 00000000000..31f290fb2da --- /dev/null +++ b/chromium/services/shell/public/cpp/lib/shell_test.cc @@ -0,0 +1,81 @@ +// 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 "services/shell/public/cpp/shell_test.h" + +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "services/shell/background/background_shell.h" +#include "services/shell/public/cpp/shell_client.h" + +namespace shell { +namespace test { + +ShellTestClient::ShellTestClient(ShellTest* test) : test_(test) {} +ShellTestClient::~ShellTestClient() {} + +void ShellTestClient::Initialize(Connector* connector, const Identity& identity, + uint32_t id) { + test_->InitializeCalled(connector, identity.name(), identity.user_id(), id); +} + +ShellTest::ShellTest() {} +ShellTest::ShellTest(const std::string& test_name) : test_name_(test_name) {} +ShellTest::~ShellTest() {} + +void ShellTest::InitTestName(const std::string& test_name) { + DCHECK(test_name_.empty()); + test_name_ = test_name; +} + +std::unique_ptr<ShellClient> ShellTest::CreateShellClient() { + return base::WrapUnique(new ShellTestClient(this)); +} + +std::unique_ptr<base::MessageLoop> ShellTest::CreateMessageLoop() { + return base::WrapUnique(new base::MessageLoop); +} + +void ShellTest::InitializeCalled(Connector* connector, + const std::string& name, + const std::string& user_id, + uint32_t id) { + DCHECK_EQ(connector_, connector); + initialize_name_ = name; + initialize_instance_id_ = id; + initialize_userid_ = user_id; + initialize_called_.Run(); +} + +void ShellTest::SetUp() { + shell_client_ = CreateShellClient(); + message_loop_ = CreateMessageLoop(); + background_shell_.reset(new shell::BackgroundShell); + background_shell_->Init(nullptr); + + // Create the shell connection. We don't proceed until we get our + // ShellClient's Initialize() method is called. + base::RunLoop run_loop; + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + initialize_called_ = run_loop.QuitClosure(); + + shell_connection_.reset(new ShellConnection( + shell_client_.get(), + background_shell_->CreateShellClientRequest(test_name_))); + connector_ = shell_connection_->connector(); + + run_loop.Run(); +} + +void ShellTest::TearDown() { + shell_connection_.reset(); + background_shell_.reset(); + message_loop_.reset(); + shell_client_.reset(); +} + +} // namespace test +} // namespace shell diff --git a/chromium/services/shell/public/cpp/names.h b/chromium/services/shell/public/cpp/names.h new file mode 100644 index 00000000000..fe534e93eb9 --- /dev/null +++ b/chromium/services/shell/public/cpp/names.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_NAMES_H_ +#define SERVICES_SHELL_PUBLIC_CPP_NAMES_H_ + +#include <string> + +namespace shell { + +extern const char kNameType_Mojo[]; +extern const char kNameType_Exe[]; + +// Mojo services and applications are identified by structured "names", of the +// form: +// +// type:path. +// +// The type field tells the shell how to load the application. Two types are +// currently recognized: +// +// mojo +// Represents an application packaged as a .mojo, launched from the +// NativeRunner launch path. .mojo files are assumed to live alongside the +// shell executable at a path matching <path>/<path>.mojo. .mojo applications +// have a MojoMain() entrypoint that receives a handle to a ShellClientRequest +// that must be bound to enable further communication with the shell. +// +// exe +// Represents a native executable on the host platform, expected to live +// alongside the shell executable. Executables launched via this mechanism are +// passed a handle to the shell on the command line and are expected to bind +// a ShellClientRequest enabling further communication with the shell. The +// path component contains the executable name, minus any platform-specific +// extension. +// +// Other types may be supplied but are not recognized by any of the +// NativeRunners, and as such custom loaders must be specified for such names. +// +// Any name type may serve as an alias for any other name type. Aliasing is +// resolved implicitly by the Shell. + +// Returns true if the name is a valid form, i.e. type:path. path cannot start +// with a "//" sequence. These are _not_ urls. +bool IsValidName(const std::string& name); + +// Get the type component of the specified name. +std::string GetNameType(const std::string& name); + +// Get the path component of the specified name. +std::string GetNamePath(const std::string& name); + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_NAMES_H_ diff --git a/chromium/services/shell/public/cpp/shell_client.h b/chromium/services/shell/public/cpp/shell_client.h new file mode 100644 index 00000000000..d9513b76e10 --- /dev/null +++ b/chromium/services/shell/public/cpp/shell_client.h @@ -0,0 +1,60 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_SHELL_CLIENT_H_ +#define SERVICES_SHELL_PUBLIC_CPP_SHELL_CLIENT_H_ + +#include <stdint.h> +#include <string> + +#include "base/macros.h" +#include "services/shell/public/cpp/connection.h" +#include "services/shell/public/cpp/identity.h" + +namespace shell { + +class Connector; + +// An interface representing an instance "known to the Mojo Shell". The +// implementation receives lifecycle messages for the instance and gets the +// opportunity to handle inbound connections brokered by the Shell. Every client +// of ShellConnection must implement this interface, and instances of this +// interface must outlive the ShellConnection. +class ShellClient { + public: + ShellClient(); + virtual ~ShellClient(); + + // Called once a bidirectional connection with the shell has been established. + // |identity| is the identity of the instance. + // |id| is a unique identifier the shell uses to identify this specific + // instance of the application. + // Called exactly once before any other method. + virtual void Initialize(Connector* connector, + const Identity& identity, + uint32_t id); + + // Called when a connection to this client is brokered by the shell. Override + // to expose services to the remote application. Return true if the connection + // should succeed. Return false if the connection should be rejected and the + // underlying pipe closed. The default implementation returns false. + virtual bool AcceptConnection(Connection* connection); + + // Called when ShellConnection's ShellClient binding (i.e. the pipe the + // Mojo Shell has to talk to us over) is closed. A shell client may use this + // as a signal to terminate. Return true from this method to tell the + // ShellConnection to run its connection lost closure if it has one, false to + // prevent it from being run. The default implementation returns true. + // When used in conjunction with ApplicationRunner, returning true here quits + // the message loop created by ApplicationRunner, which results in the app + // quitting. + virtual bool ShellConnectionLost(); + + private: + DISALLOW_COPY_AND_ASSIGN(ShellClient); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_SHELL_CLIENT_H_ diff --git a/chromium/services/shell/public/cpp/shell_connection.h b/chromium/services/shell/public/cpp/shell_connection.h new file mode 100644 index 00000000000..f2f0cfc5129 --- /dev/null +++ b/chromium/services/shell/public/cpp/shell_connection.h @@ -0,0 +1,107 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_SHELL_CONNECTION_H_ +#define SERVICES_SHELL_PUBLIC_CPP_SHELL_CONNECTION_H_ + +#include <memory> +#include <utility> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_vector.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/system/core.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/interfaces/connector.mojom.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" + +namespace shell { + +class Connector; + +// Encapsulates a connection to the Mojo Shell in two parts: +// - a bound InterfacePtr to mojom::Shell, the primary mechanism +// by which the instantiating application interacts with other services +// brokered by the Mojo Shell. +// - a bound InterfaceRequest of mojom::ShellClient, an interface +// used by the Mojo Shell to inform this application of lifecycle events and +// inbound connections brokered by it. +// +// This class should be used in two scenarios: +// - During early startup to bind the mojom::ShellClientRequest obtained from +// the Mojo Shell, typically in response to either MojoMain() or main(). +// - In an implementation of mojom::ShellClientFactory to bind the +// mojom::ShellClientRequest passed via StartApplication. In this scenario +// there can be many instances of this class per process. +// +// Instances of this class are constructed with an implementation of the Shell +// Client Lib's ShellClient interface. See documentation in shell_client.h +// for details. +// +class ShellConnection : public mojom::ShellClient { + public: + // Creates a new ShellConnection bound to |request|. This connection may be + // used immediately to make outgoing connections via connector(). Does not + // take ownership of |client|, which must remain valid for the lifetime of + // ShellConnection. + ShellConnection(shell::ShellClient* client, + mojom::ShellClientRequest request); + + ~ShellConnection() override; + + Connector* connector() { return connector_.get(); } + const Identity& identity() { return identity_; } + + // TODO(rockot): Remove this. http://crbug.com/594852. + void set_initialize_handler(const base::Closure& callback); + + // TODO(rockot): Remove this once we get rid of app tests. + void SetAppTestConnectorForTesting(mojom::ConnectorPtr connector); + + // Specify a function to be called when the connection to the shell is lost. + // Note that if connection has already been lost, then |closure| is called + // immediately. + void SetConnectionLostClosure(const base::Closure& closure); + + private: + // mojom::ShellClient: + void Initialize(mojom::IdentityPtr identity, + uint32_t id, + const InitializeCallback& callback) override; + void AcceptConnection(mojom::IdentityPtr source, + uint32_t source_id, + mojom::InterfaceProviderRequest remote_interfaces, + mojom::InterfaceProviderPtr local_interfaces, + mojom::CapabilityRequestPtr allowed_capabilities, + const mojo::String& name) override; + + void OnConnectionError(); + + // A callback called when Initialize() is run. + base::Closure initialize_handler_; + + // We track the lifetime of incoming connection registries as it more + // convenient for the client. + ScopedVector<Connection> incoming_connections_; + + // A pending Connector request which will eventually be passed to the shell. + mojom::ConnectorRequest pending_connector_request_; + + shell::ShellClient* client_; + mojo::Binding<mojom::ShellClient> binding_; + std::unique_ptr<Connector> connector_; + shell::Identity identity_; + bool should_run_connection_lost_closure_ = false; + + base::Closure connection_lost_closure_; + + DISALLOW_COPY_AND_ASSIGN(ShellConnection); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_SHELL_CONNECTION_H_ diff --git a/chromium/services/shell/public/cpp/shell_connection_ref.h b/chromium/services/shell/public/cpp/shell_connection_ref.h new file mode 100644 index 00000000000..b22e12ea3d6 --- /dev/null +++ b/chromium/services/shell/public/cpp/shell_connection_ref.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_SHELL_CONNECTION_REF_H_ +#define SERVICES_SHELL_PUBLIC_CPP_SHELL_CONNECTION_REF_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" + +namespace shell { + +class ShellConnectionRefImpl; + +// An interface implementation can keep this object as a member variable to +// hold a reference to the ShellConnection, keeping it alive as long as the +// bound implementation exists. +// +// This class is safe to use on any thread and instances may be passed to other +// threads. However, each instance should only be used on one thread at a time, +// otherwise there'll be races between the AddRef resulting from cloning and +// destruction. +class ShellConnectionRef { + public: + virtual ~ShellConnectionRef() {} + + virtual std::unique_ptr<ShellConnectionRef> Clone() = 0; +}; + +class ShellConnectionRefFactory { + public: + // |quit_closure| is called whenever the last ref is destroyed. + explicit ShellConnectionRefFactory(const base::Closure& quit_closure); + ~ShellConnectionRefFactory(); + + std::unique_ptr<ShellConnectionRef> CreateRef(); + + bool HasNoRefs() const { return !ref_count_; } + + private: + friend ShellConnectionRefImpl; + + // Called from ShellConnectionRefImpl. + void AddRef(); + void Release(); + + const base::Closure quit_closure_; + int ref_count_ = 0; + base::WeakPtrFactory<ShellConnectionRefFactory> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ShellConnectionRefFactory); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_SHELL_CONNECTION_REF_H_ diff --git a/chromium/services/shell/public/cpp/shell_test.h b/chromium/services/shell/public/cpp/shell_test.h new file mode 100644 index 00000000000..b299297a4c9 --- /dev/null +++ b/chromium/services/shell/public/cpp/shell_test.h @@ -0,0 +1,112 @@ +// 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. + +#ifndef SERVICES_SHELL_PUBLIC_CPP_SHELL_TEST_H_ +#define SERVICES_SHELL_PUBLIC_CPP_SHELL_TEST_H_ + +#include <memory> + +#include "base/macros.h" +#include "services/shell/public/cpp/connector.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/cpp/shell_connection.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +class MessageLoop; +} + +namespace shell { + +class BackgroundShell; + +namespace test { + +class ShellTest; + +// A default implementation of ShellClient for use in ShellTests. Tests wishing +// to customize this should subclass this class instead of ShellClient, +// otherwise they will have to call ShellTest::InitializeCalled() to forward +// metadata from Initialize() to the test. +class ShellTestClient : public ShellClient { + public: + explicit ShellTestClient(ShellTest* test); + ~ShellTestClient() override; + + protected: + void Initialize(Connector* connector, + const Identity& identity, + uint32_t id) override; + + private: + ShellTest* test_; + + DISALLOW_COPY_AND_ASSIGN(ShellTestClient); +}; + +class ShellTest : public testing::Test { + public: + ShellTest(); + // Initialize passing the name to use as the identity for the test itself. + // Once set via this constructor, it cannot be changed later by calling + // InitTestName(). The test executable must provide a manifest in the + // appropriate location that specifies this name also. + explicit ShellTest(const std::string& test_name); + ~ShellTest() override; + + protected: + // See constructor. Can only be called once. + void InitTestName(const std::string& test_name); + + Connector* connector() { return connector_; } + + // Instance information received from the Shell during Initialize(). + const std::string& test_name() const { return initialize_name_; } + const std::string& test_userid() const { return initialize_userid_; } + uint32_t test_instance_id() const { return initialize_instance_id_; } + + // By default, creates a simple ShellClient that captures the metadata sent + // via Initialize(). Override to customize, but custom implementations must + // call InitializeCalled() to forward the metadata so test_name() etc all + // work. + virtual std::unique_ptr<ShellClient> CreateShellClient(); + + virtual std::unique_ptr<base::MessageLoop> CreateMessageLoop(); + + // Call to set Initialize() metadata when GetShellClient() is overridden. + void InitializeCalled(Connector* connector, + const std::string& name, + const std::string& userid, + uint32_t id); + + // testing::Test: + void SetUp() override; + void TearDown() override; + + private: + friend ShellTestClient; + + std::unique_ptr<ShellClient> shell_client_; + + std::unique_ptr<base::MessageLoop> message_loop_; + std::unique_ptr<BackgroundShell> background_shell_; + std::unique_ptr<ShellConnection> shell_connection_; + + // See constructor. + std::string test_name_; + + Connector* connector_ = nullptr; + std::string initialize_name_; + std::string initialize_userid_ = shell::mojom::kInheritUserID; + uint32_t initialize_instance_id_ = shell::mojom::kInvalidInstanceID; + + base::Closure initialize_called_; + + DISALLOW_COPY_AND_ASSIGN(ShellTest); +}; + +} // namespace test +} // namespace shell + +#endif // SERVICES_SHELL_PUBLIC_CPP_SHELL_TEST_H_ diff --git a/chromium/services/shell/public/interfaces/BUILD.gn b/chromium/services/shell/public/interfaces/BUILD.gn new file mode 100644 index 00000000000..5a2ef6ce1cd --- /dev/null +++ b/chromium/services/shell/public/interfaces/BUILD.gn @@ -0,0 +1,22 @@ +# Copyright 2014 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +# GYP version: mojo/mojo_base.gyp:mojo_application_bindings +mojom("interfaces") { + sources = [ + "capabilities.mojom", + "connector.mojom", + "interface_provider.mojom", + "shell.mojom", + "shell_client.mojom", + "shell_client_factory.mojom", + "shell_resolver.mojom", + ] + + public_deps = [ + "//mojo/common:common_custom_types", + ] +} diff --git a/chromium/services/shell/public/interfaces/OWNERS b/chromium/services/shell/public/interfaces/OWNERS new file mode 100644 index 00000000000..9e621796752 --- /dev/null +++ b/chromium/services/shell/public/interfaces/OWNERS @@ -0,0 +1,13 @@ +# Changes to Mojo interfaces require a security review to avoid +# introducing new sandbox escapes. +per-file *.mojom=set noparent +per-file *.mojom=dcheng@chromium.org +per-file *.mojom=inferno@chromium.org +per-file *.mojom=jln@chromium.org +per-file *.mojom=jschuh@chromium.org +per-file *.mojom=kenrb@chromium.org +per-file *.mojom=mkwst@chromium.org +per-file *.mojom=nasko@chromium.org +per-file *.mojom=palmer@chromium.org +per-file *.mojom=tsepez@chromium.org +per-file *.mojom=wfh@chromium.org diff --git a/chromium/services/shell/public/interfaces/capabilities.mojom b/chromium/services/shell/public/interfaces/capabilities.mojom new file mode 100644 index 00000000000..dff92305f4b --- /dev/null +++ b/chromium/services/shell/public/interfaces/capabilities.mojom @@ -0,0 +1,47 @@ +// 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. + +module shell.mojom; + +// Mojo Capabilities ----------------------------------------------------------- +// +// Mojo applications expose interfaces and capability classes to one another. +// +// An interface is just a Mojo interface, defined in a mojom file like this one. +// In a CapabilitySpec, an interface is represented by its fully qualified name, +// which is serialized based on the interface name and module: +// module::path::InterfaceName. +// +// A class is an alias to something, either a set of interface names granted +// with that class, or some behavior specific to the application that provides +// it. + +// Describes the set of classes and interfaces required by an application. +// Note that there may be overlap between the interfaces implied by the +// resolution of classes and those specified in |interfaces|. The set of +// interfaces granted to the requestor is the union of these sets. +struct CapabilityRequest { + // An array of class names required. + array<string> classes; + // An array of interface names required. + array<string> interfaces; +}; + +// Describes the capabilities offered and requested by an application. +// This struct is populated from the application manifest. +struct CapabilitySpec { + // The classes offered by this application, and for each class an array of + // interfaces. If no interfaces are granted with a class, the array will be + // empty. + // A map of class name -> array of interfaces. The array can be empty, + // non-empty, or ["*"], which means allow access to all interfaces. + map<string, array<string>> provided; + + // The applications this application needs to speak to, and the classes and + // interfaces it requests. + // A map of application name -> spec. "*" is also supported as the key, which + // supplies a CapabilityRequest for all applications in addition to specific + // ones specified. + map<string, CapabilityRequest> required; +}; diff --git a/chromium/services/shell/public/interfaces/connector.mojom b/chromium/services/shell/public/interfaces/connector.mojom new file mode 100644 index 00000000000..f419ebee4aa --- /dev/null +++ b/chromium/services/shell/public/interfaces/connector.mojom @@ -0,0 +1,144 @@ +// Copyright 2014 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. + +module shell.mojom; + +import "services/shell/public/interfaces/interface_provider.mojom"; + +const string kRootUserID = "505C0EE9-3013-43C0-82B0-A84F50CF8D84"; +const string kInheritUserID = "D26290E4-4485-4EAE-81A2-66D1EEB40A9D"; + +const uint32 kInvalidInstanceID = 0; + +enum ConnectResult { + // The connection was established successfully. + SUCCEEDED, + + // The name or user id supplied was malformed, or the application specified + // by |name| could not be loaded. + INVALID_ARGUMENT, + + // The connection was blocked by policy. Either connections to |name| are + // forbidden from this app by the CapabilityFilter, or the application + // attempted to connect using a user id other than its own, + // kInheritUserID or kRootUserID. + ACCESS_DENIED +}; + +// A collection of metadata that disambiguates instances in the shell. +struct Identity { + // A mojo: or exe: name identifying an application. + string name; + + // The user id of the target application instance to connect to. If no such + // instance exists, the shell may start one. This user id will be passed to + // the new instance via Initialize(). + // When connecting to other applications, applications must generally pass + // kInheritUserID for this value, and the shell will either connect to an + // existing instance matching the caller's user id, create a new instance + // matching the caller's user id, or connect to an existing instance running + // as kRootUserID. By default, applications do not have the ability to set + // arbitrary values to this field, and doing so will result in a connection + // error on the remote service provider. An application with the ability to + // launch applications with arbitrary user ids (e.g. a login app) may set this + // value to something meaningful to it. The user id string is a valid guid of + // the form "%08X-%04X-%04X-%04X-%012llX", and (aside from the root user whose + // guid is defined above) intended to be not-guessable. + // When an application is initialized or receives a connection from another + // application, this value is always the resolved user id, never + // kInheritUserID. + string user_id; + + // An application may spawn multiple instances with the same name,user_id + // pair, provided they are started with unique values of this field. + // TODO(beng): enforce the emptiness of this parameter unless the client bears + // the appropriate capability. + string instance; +}; + +// Implemented by an object in the shell associated with a specific instance. +// Tells it the PID for a process launched by the client. See +// ClientProcessConnection. +interface PIDReceiver { + SetPID(uint32 pid); +}; + +// Typically, the shell will start a process for a service the first time it +// receives a connection request for it. This struct allows a client to start +// the process itself and provide the shell the pipes it needs to communicate +// with it. When an instance of this struct is supplied to Connect(), the client +// owns the lifetime of the child process, not the shell. The shell binds the +// |shell_client| pipe, and when it closes destroys the associated instance but +// the process stays alive. +struct ClientProcessConnection { + // Provides the shell the ability to bind a ShellClient from the client + // process to the instance it creates. + handle<message_pipe> shell_client; + + // Allows the client process launcher to tell the shell the PID of the process + // it created (the pid isn't supplied directly here as the process may not + // have been launched by the time Connect() is called.) + handle<message_pipe> pid_receiver_request; +}; + +// Encapsulates establishing connections with other Mojo applications. +interface Connector { + // Requests a connection with another application. The application originating + // the request is referred to as the "source" and the one receiving the + // "target". + // + // The connection is embodied by a pair of message pipes binding the + // InterfaceProvider interface, which allows both the source and target + // applications to export interfaces to one another. The interfaces bound via + // these InterfaceProviders are brokered by the shell according to the + // security policy defined by each application in its manifest . + // + // If the target application is not running, the shell will run it, calling + // its Initialize() method before completing the connection. + // + // Parameters: + // + // target + // Identifies the target application instance to connect to. + // + // remote_interfaces + // Allows the source application access to interface implementations + // exposed by the target application. The interfaces accessible via this + // InterfaceParameter are filtered by the security policy described by the + // source and target application manifests. + // + // local_interfaces + // Allows the remote application access to interface implementations + // exposed by the source application. The interfaces accessible via this + // InterfaceProvider are filtered by the security policy described by the + // source and target application manifests. + // + // client_process_connection + // When non-null, supplies control pipes the shell can use to bind a + // process created by the client, instead of creating one itself. + // TODO(beng): access to this parameter should be restricted by a + // capability. + // + // Response parameters: + // + // result + // Indicates the result of the Connect() operation. + // + // user_id + // The user id the shell ran the target application as. Typically a client + // passes kInheritUserID as the user id to Connect() which is resolved by + // the shell into a valid user id returned through this callback. + // + // application_id + // A unique identifier for the instance that was connected to. + // + Connect(Identity target, + InterfaceProvider&? remote_interfaces, + InterfaceProvider? local_interfaces, + ClientProcessConnection? client_process_connection) => + (ConnectResult result, string user_id, uint32 application_id); + + // Clones this Connector so it can be passed to another thread. + Clone(Connector& request); +}; diff --git a/chromium/services/shell/public/interfaces/interface_provider.mojom b/chromium/services/shell/public/interfaces/interface_provider.mojom new file mode 100644 index 00000000000..c64f52f637a --- /dev/null +++ b/chromium/services/shell/public/interfaces/interface_provider.mojom @@ -0,0 +1,17 @@ +// Copyright 2014 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. + +module shell.mojom; + +// An interface through which a client may request interfaces from a host. +// Instances of this interface are created within the context of an +// already-identified client and host pair, so there is no need to explicitly +// identify the client or host in the methods below. +interface InterfaceProvider { + // Asks the host to provide an implementation of the interface identified by + // |interface_name| through the message |pipe| endpoint supplied by the caller. + // If the host is not willing or able to provide the requested interface, it + // must close the |pipe|. + GetInterface(string interface_name, handle<message_pipe> pipe); +}; diff --git a/chromium/services/shell/public/interfaces/shell.mojom b/chromium/services/shell/public/interfaces/shell.mojom new file mode 100644 index 00000000000..2ff9201882d --- /dev/null +++ b/chromium/services/shell/public/interfaces/shell.mojom @@ -0,0 +1,41 @@ +// 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. + +module shell.mojom; + +import "services/shell/public/interfaces/connector.mojom"; + +struct InstanceInfo { + uint32 id; + Identity identity; + uint32 pid; +}; + +// Implemented by a client that wishes to be informed when the list of running +// instances changes. +interface InstanceListener { + // Called once when the listener is added via Shell::AddInstanceListener() to + // provide the initial list of instances that the listener observes changes + // against. + SetExistingInstances(array<InstanceInfo> instances); + + // Called when the shell has started tracking an instance. This happens when + // the shell first handles a request to launch the instance, before any + // process is created for it. + InstanceCreated(InstanceInfo instance); + + // Called when the shell has stopped tracking an instance. (i.e. when it has + // ended/quit). + InstanceDestroyed(uint32 id); + + // Called when a pid is available for the instance. This could be because a + // process was created by the runner for it, or because an existing content + // handler process was assigned. + InstancePIDAvailable(uint32 id, uint32 pid); +}; + +interface Shell { + // The listener is removed when the |listener| pipe is closed. + AddInstanceListener(InstanceListener listener); +}; diff --git a/chromium/services/shell/public/interfaces/shell_client.mojom b/chromium/services/shell/public/interfaces/shell_client.mojom new file mode 100644 index 00000000000..04618cac063 --- /dev/null +++ b/chromium/services/shell/public/interfaces/shell_client.mojom @@ -0,0 +1,78 @@ +// Copyright 2014 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. + +module shell.mojom; + +import "services/shell/public/interfaces/capabilities.mojom"; +import "services/shell/public/interfaces/connector.mojom"; +import "services/shell/public/interfaces/interface_provider.mojom"; + +// Implemented by something "known to" the Mojo Shell (e.g. an application or +// service), for which an instance is tracked. It allows the implementor to +// receive lifecycle events and service inbound connection attempts. +interface ShellClient { + // Called by the shell once an instance for this application has been created. + // This method will be called exactly once before any other method is called. + // + // Parameters: + // + // identity + // The identity of this instance in the shell. Includes: + // * The resolved name used in the connection request that resulted in this + // instance being initialized. + // * The user associated with this instance in the shell. This will never + // be kInheritUserID. + // * The instance group this instance belongs to. + // + // id + // A unique identifier used by the shell to identify this instance. + // + // + // Response parameters: + // + // connector_request + // An optional Connector request for the shell to bind, allowing the + // initialized client to connect to others. + // + Initialize(Identity identity, uint32 id) => (Connector&? connector_request); + + // Called when another application attempts to open a connection to this + // application. An application implements this method to complete the exchange + // of interface implementations with the remote application. See also + // documentation in shell.mojom for Connect(). The application originating + // the request is referred to as the "source" and the one receiving the + // "target". + // + // Parameters: + // + // source + // The identity of the instance originating the connection. + // + // source_id + // A unique identifier used by the shell to identify the source instance. + // + // local_interfaces + // A request for an InterfaceProvider by which the source application may + // seek to bind interface implementations exported by the target. + // + // remote_interfaces + // An InterfaceProvider by which the target application may bind interface + // implementations exported by the source. + // + // allowed_interfaces + // A whitelist of interface names that should be exported to the source, + // according to the security policy described by the source and target's + // manifests. Attempts to bind interfaces not in this whitelist must not be + // fulfilled. + // + // resolved_name + // The resolved name used to complete this connection. + // + AcceptConnection(Identity source, + uint32 source_id, + InterfaceProvider&? local_interfaces, + InterfaceProvider? remote_interfaces, + CapabilityRequest allowed_capabilities, + string resolved_name); +}; diff --git a/chromium/services/shell/public/interfaces/shell_client_factory.mojom b/chromium/services/shell/public/interfaces/shell_client_factory.mojom new file mode 100644 index 00000000000..a159389db52 --- /dev/null +++ b/chromium/services/shell/public/interfaces/shell_client_factory.mojom @@ -0,0 +1,13 @@ +// 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. + +module shell.mojom; + +import "services/shell/public/interfaces/shell_client.mojom"; + +// Implemented by a package containing multiple applications identified by +// unique names. +interface ShellClientFactory { + CreateShellClient(ShellClient& shell_client, string name); +}; diff --git a/chromium/services/shell/public/interfaces/shell_resolver.mojom b/chromium/services/shell/public/interfaces/shell_resolver.mojom new file mode 100644 index 00000000000..3483aa68b4d --- /dev/null +++ b/chromium/services/shell/public/interfaces/shell_resolver.mojom @@ -0,0 +1,38 @@ +// 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. + +module shell.mojom; + +import "mojo/common/common_custom_types.mojom"; +import "services/shell/public/interfaces/capabilities.mojom"; + +// The result of a Resolve operation via ShellResolver. +struct ResolveResult { + // The mojo: name that was requested to be resolved. + string name; + + // The mojo: name of the physical package supplying the requested name. This + // could be the same name that was passed, or the name of a package that + // contains it. + string resolved_name; + + // An additional piece of metadata that identifies what instance |name| should + // be run in. It's possible that |name| may provide several services that + // should be run as different instances. + string qualifier; + + // The set of capabilities provided and required by |name|. + CapabilitySpec capabilities; + + // A path to the package file specified by |name|. + mojo.common.mojom.FilePath package_path; +}; + +// Implemented exclusively for the Mojo Shell's use in resolving mojo: names +// and reading static manifest information. +interface ShellResolver { + // Resolves |mojo_name| and returns a ResolveResult containing metadata from + // the catalog that the Shell uses to run an instance of it. + ResolveMojoName(string mojo_name) => (ResolveResult result); +}; diff --git a/chromium/services/shell/public/java/BUILD.gn b/chromium/services/shell/public/java/BUILD.gn new file mode 100644 index 00000000000..925916fb172 --- /dev/null +++ b/chromium/services/shell/public/java/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2014 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. + +import("//build/config/android/rules.gni") + +android_library("application") { + java_files = [ + "src/org/chromium/mojo/application/ApplicationConnection.java", + "src/org/chromium/mojo/application/ApplicationDelegate.java", + "src/org/chromium/mojo/application/ApplicationImpl.java", + "src/org/chromium/mojo/application/ApplicationRunner.java", + "src/org/chromium/mojo/application/ServiceFactoryBinder.java", + "src/org/chromium/mojo/application/ShellHelper.java", + ] + deps = [ + "//mojo/public/java:bindings", + "//mojo/public/java:system", + "//services/shell/public/interfaces:interfaces_java", + ] +} diff --git a/chromium/services/shell/runner/BUILD.gn b/chromium/services/shell/runner/BUILD.gn new file mode 100644 index 00000000000..65520053a58 --- /dev/null +++ b/chromium/services/shell/runner/BUILD.gn @@ -0,0 +1,25 @@ +# 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. + +group("runner") { + testonly = true + deps = [ + "//services/shell/runner/child", + "//services/shell/runner/host", + ] +} + +source_set("init") { + sources = [ + "init.cc", + "init.h", + ] + + deps = [ + "//base", + "//base:base_static", + "//base:i18n", + "//services/shell/runner/common", + ] +} diff --git a/chromium/services/shell/runner/child/BUILD.gn b/chromium/services/shell/runner/child/BUILD.gn new file mode 100644 index 00000000000..197a0a46e42 --- /dev/null +++ b/chromium/services/shell/runner/child/BUILD.gn @@ -0,0 +1,32 @@ +# 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. + +import("//mojo/public/mojo_application.gni") +import("//mojo/public/mojo_application_manifest.gni") +import("//mojo/public/tools/bindings/mojom.gni") + +group("child") { + testonly = true + deps = [ + ":test_native_main", + ] +} + +source_set("test_native_main") { + sources = [ + "test_native_main.cc", + "test_native_main.h", + ] + + public_deps = [ + "//services/shell/runner:init", + ] + + deps = [ + "//base", + "//mojo/edk/system", + "//services/shell/public/cpp", + "//services/shell/runner/common", + ] +} diff --git a/chromium/services/shell/runner/child/manifest.json b/chromium/services/shell/runner/child/manifest.json new file mode 100644 index 00000000000..a6a557a0d3a --- /dev/null +++ b/chromium/services/shell/runner/child/manifest.json @@ -0,0 +1,10 @@ +{ + "manifest_version": 1, + "name": "mojo:mojo_runner_child_apptest", + "display_name": "Runner Child Apptest", + "capabilities": { + "required": { + "*": { "classes": [ "app" ] } + } + } +} diff --git a/chromium/services/shell/runner/child/test_native_main.cc b/chromium/services/shell/runner/child/test_native_main.cc new file mode 100644 index 00000000000..ea5bd0c96bb --- /dev/null +++ b/chromium/services/shell/runner/child/test_native_main.cc @@ -0,0 +1,70 @@ +// 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 "services/shell/runner/child/test_native_main.h" + +#include <utility> + +#include "base/debug/stack_trace.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/process/launch.h" +#include "base/threading/thread.h" +#include "build/build_config.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/process_delegate.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/cpp/shell_connection.h" +#include "services/shell/runner/common/client_util.h" +#include "services/shell/runner/init.h" + +namespace shell { +namespace { + +class ProcessDelegate : public mojo::edk::ProcessDelegate { + public: + ProcessDelegate() {} + ~ProcessDelegate() override {} + + private: + void OnShutdownComplete() override {} + + DISALLOW_COPY_AND_ASSIGN(ProcessDelegate); +}; + +} // namespace + +int TestNativeMain(shell::ShellClient* shell_client) { + shell::WaitForDebuggerIfNecessary(); + +#if !defined(OFFICIAL_BUILD) + base::debug::EnableInProcessStackDumping(); +#if defined(OS_WIN) + base::RouteStdioToConsole(false); +#endif +#endif + + { + mojo::edk::Init(); + + ProcessDelegate process_delegate; + base::Thread io_thread("io_thread"); + base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0); + CHECK(io_thread.StartWithOptions(io_thread_options)); + + mojo::edk::InitIPCSupport(&process_delegate, io_thread.task_runner()); + mojo::edk::SetParentPipeHandleFromCommandLine(); + + base::MessageLoop loop; + shell::ShellConnection impl(shell_client, + shell::GetShellClientRequestFromCommandLine()); + loop.Run(); + + mojo::edk::ShutdownIPCSupport(); + } + + return 0; +} + +} // namespace shell diff --git a/chromium/services/shell/runner/child/test_native_main.h b/chromium/services/shell/runner/child/test_native_main.h new file mode 100644 index 00000000000..a8956166697 --- /dev/null +++ b/chromium/services/shell/runner/child/test_native_main.h @@ -0,0 +1,16 @@ +// 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. + +#ifndef SERVICES_SHELL_RUNNER_CHILD_TEST_NATIVE_MAIN_H_ +#define SERVICES_SHELL_RUNNER_CHILD_TEST_NATIVE_MAIN_H_ + +namespace shell { + +class ShellClient; + +int TestNativeMain(ShellClient* shell_client); + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_CHILD_TEST_NATIVE_MAIN_H_ diff --git a/chromium/services/shell/runner/common/BUILD.gn b/chromium/services/shell/runner/common/BUILD.gn new file mode 100644 index 00000000000..4bb5387fbdc --- /dev/null +++ b/chromium/services/shell/runner/common/BUILD.gn @@ -0,0 +1,23 @@ +# 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. + +source_set("common") { + sources = [ + "client_util.cc", + "client_util.h", + "switches.cc", + "switches.h", + ] + + deps = [ + "//base", + "//mojo/edk/system", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/system", + ] + + public_deps = [ + "//services/shell/public/interfaces", + ] +} diff --git a/chromium/services/shell/runner/common/client_util.cc b/chromium/services/shell/runner/common/client_util.cc new file mode 100644 index 00000000000..9a79c0e88db --- /dev/null +++ b/chromium/services/shell/runner/common/client_util.cc @@ -0,0 +1,36 @@ +// 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 "services/shell/runner/common/client_util.h" + +#include <string> + +#include "base/command_line.h" +#include "mojo/edk/embedder/embedder.h" +#include "services/shell/runner/common/switches.h" + +namespace shell { + +mojom::ShellClientPtr PassShellClientRequestOnCommandLine( + base::CommandLine* command_line) { + std::string token = mojo::edk::GenerateRandomToken(); + command_line->AppendSwitchASCII(switches::kPrimordialPipeToken, token); + + mojom::ShellClientPtr client; + client.Bind( + mojom::ShellClientPtrInfo(mojo::edk::CreateParentMessagePipe(token), 0)); + return client; +} + +mojom::ShellClientRequest GetShellClientRequestFromCommandLine() { + std::string token = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kPrimordialPipeToken); + mojom::ShellClientRequest request; + if (!token.empty()) + request.Bind(mojo::edk::CreateChildMessagePipe(token)); + return request; +} + +} // namespace shell diff --git a/chromium/services/shell/runner/common/client_util.h b/chromium/services/shell/runner/common/client_util.h new file mode 100644 index 00000000000..d1ff4ff3be8 --- /dev/null +++ b/chromium/services/shell/runner/common/client_util.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef SERVICES_SHELL_RUNNER_COMMON_CLIENT_UTIL_H_ +#define SERVICES_SHELL_RUNNER_COMMON_CLIENT_UTIL_H_ + +#include "services/shell/public/interfaces/shell_client.mojom.h" + +namespace base { +class CommandLine; +} + +namespace shell { + +// Creates a new ShellClient pipe and returns one end of it. The other end is +// passed via a token in |command_line|. A child of the calling process may +// extract a ShellClientRequest from this by calling +// GetShellClientRequestFromCommandLine(). +mojom::ShellClientPtr PassShellClientRequestOnCommandLine( + base::CommandLine* command_line); + +// Extracts a ShellClientRequest from the command line of the current process. +// The parent of this process should have passed a request using +// PassShellClientRequestOnCommandLine(). +mojom::ShellClientRequest GetShellClientRequestFromCommandLine(); + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_COMMON_CLIENT_UTIL_H_ diff --git a/chromium/services/shell/runner/common/switches.cc b/chromium/services/shell/runner/common/switches.cc new file mode 100644 index 00000000000..545123781a3 --- /dev/null +++ b/chromium/services/shell/runner/common/switches.cc @@ -0,0 +1,21 @@ +// 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 "services/shell/runner/common/switches.h" + +namespace switches { + +// Used internally by the main process to indicate that a new process should be +// a child process. Takes the absolute path to the mojo application to load as +// an argument. Not for user use. +const char kChildProcess[] = "child-process"; + +// Enables the sandbox on this process. +const char kEnableSandbox[] = "enable-sandbox"; + +// Provides a child process with a token string they can use to establish a +// primordial message pipe to the parent. +const char kPrimordialPipeToken[] = "primordial-pipe-token"; + +} // namespace switches diff --git a/chromium/services/shell/runner/common/switches.h b/chromium/services/shell/runner/common/switches.h new file mode 100644 index 00000000000..4406b07acce --- /dev/null +++ b/chromium/services/shell/runner/common/switches.h @@ -0,0 +1,18 @@ +// 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. + +#ifndef SERVICES_SHELL_RUNNER_COMMON_SWITCHES_H_ +#define SERVICES_SHELL_RUNNER_COMMON_SWITCHES_H_ + +namespace switches { + +// All switches in alphabetical order. The switches should be documented +// alongside the definition of their values in the .cc file. +extern const char kChildProcess[]; +extern const char kEnableSandbox[]; +extern const char kPrimordialPipeToken[]; + +} // namespace switches + +#endif // SERVICES_SHELL_RUNNER_COMMON_SWITCHES_H_ diff --git a/chromium/services/shell/runner/host/BUILD.gn b/chromium/services/shell/runner/host/BUILD.gn new file mode 100644 index 00000000000..b7658de33cc --- /dev/null +++ b/chromium/services/shell/runner/host/BUILD.gn @@ -0,0 +1,126 @@ +# 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. + +import("//mojo/public/mojo_application.gni") +import("//mojo/public/tools/bindings/mojom.gni") +import("//testing/test.gni") + +group("host") { + testonly = true + + deps = [ + ":lib", + ":mojo_runner_host_unittests", + ] +} + +source_set("native_application_support") { + sources = [ + "native_application_support.cc", + "native_application_support.h", + ] + + deps = [ + "//base", + "//mojo/platform_handle:platform_handle_impl", + "//services/shell", + ] + + # This target has to include the public thunk headers, which generally + # shouldn't be included without picking an implementation. We are providing + # the implementation but the thunk header target cannot declare that we are + # permitted to include it since it's in the public SDK and we are not. + # Suppress include checking so we can still check the rest of the targets in + # this file. + check_includes = false +} + +source_set("child_process_base") { + sources = [ + "child_process_base.cc", + "child_process_base.h", + ] + + deps = [ + "//base", + "//mojo/edk/system", + "//mojo/platform_handle:platform_handle_impl", + "//services/shell", + "//services/shell/public/interfaces", + "//services/shell/runner:init", + "//services/shell/runner/common", + ] +} + +source_set("lib") { + sources = [ + "child_process.cc", + "child_process.h", + "child_process_host.cc", + "child_process_host.h", + "in_process_native_runner.cc", + "in_process_native_runner.h", + "out_of_process_native_runner.cc", + "out_of_process_native_runner.h", + ] + + deps = [ + ":child_process_base", + ":native_application_support", + "//base:base_static", + "//base:i18n", + "//mojo/platform_handle:platform_handle_impl", + "//services/shell/public/cpp:sources", + "//services/shell/runner:init", + "//services/shell/runner/common", + ] + + public_deps = [ + "//base", + "//mojo/edk/system", + "//mojo/public/cpp/system", + "//services/shell", + "//services/shell/public/interfaces", + ] + + if (is_linux && !is_android) { + sources += [ + "linux_sandbox.cc", + "linux_sandbox.h", + ] + + deps += [ + "//sandbox/linux:sandbox", + "//sandbox/linux:sandbox_services", + "//sandbox/linux:seccomp_bpf", + "//sandbox/linux:seccomp_bpf_helpers", + ] + } + + if (is_mac) { + sources += [ + "mach_broker.cc", + "mach_broker.h", + ] + } +} + +test("mojo_runner_host_unittests") { + sources = [ + "child_process_host_unittest.cc", + "host_unittests.cc", + "in_process_native_runner_unittest.cc", + ] + + deps = [ + ":lib", + "//base", + "//base/test:test_support", + "//mojo/edk/system", + "//services/shell", + "//services/shell/runner:init", + "//services/shell/runner/common", + "//testing/gtest", + ] +} diff --git a/chromium/services/shell/runner/host/DEPS b/chromium/services/shell/runner/host/DEPS new file mode 100644 index 00000000000..ec69c8f59a8 --- /dev/null +++ b/chromium/services/shell/runner/host/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+sandbox", +] diff --git a/chromium/services/shell/runner/host/OWNERS b/chromium/services/shell/runner/host/OWNERS new file mode 100644 index 00000000000..2b40a92356e --- /dev/null +++ b/chromium/services/shell/runner/host/OWNERS @@ -0,0 +1 @@ +per-file linux_sandbox*=rickyz@chromium.org diff --git a/chromium/services/shell/runner/host/child_process.cc b/chromium/services/shell/runner/host/child_process.cc new file mode 100644 index 00000000000..836adc8fa19 --- /dev/null +++ b/chromium/services/shell/runner/host/child_process.cc @@ -0,0 +1,126 @@ +// Copyright 2014 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 "services/shell/runner/host/child_process.h" + +#include <stdint.h> + +#include <memory> +#include <utility> + +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/command_line.h" +#include "base/debug/stack_trace.h" +#include "base/files/file_path.h" +#include "base/i18n/icu_util.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/threading/thread_checker.h" +#include "base/threading/thread_task_runner_handle.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/platform_channel_pair.h" +#include "mojo/edk/embedder/process_delegate.h" +#include "mojo/edk/embedder/scoped_platform_handle.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/system/core.h" +#include "services/shell/runner/common/switches.h" +#include "services/shell/runner/host/child_process_base.h" +#include "services/shell/runner/host/native_application_support.h" +#include "services/shell/runner/init.h" + +#if defined(OS_LINUX) && !defined(OS_ANDROID) +#include "base/rand_util.h" +#include "base/sys_info.h" +#include "services/shell/runner/host/linux_sandbox.h" +#endif + +#if defined(OS_MACOSX) +#include "services/shell/runner/host/mach_broker.h" +#endif + +namespace shell { + +namespace { + +#if defined(OS_LINUX) && !defined(OS_ANDROID) +std::unique_ptr<LinuxSandbox> InitializeSandbox() { + using sandbox::syscall_broker::BrokerFilePermission; + // Warm parts of base in the copy of base in the mojo runner. + base::RandUint64(); + base::SysInfo::AmountOfPhysicalMemory(); + base::SysInfo::MaxSharedMemorySize(); + base::SysInfo::NumberOfProcessors(); + + // TODO(erg,jln): Allowing access to all of /dev/shm/ makes it easy to + // spy on other shared memory using processes. This is a temporary hack + // so that we have some sandbox until we have proper shared memory + // support integrated into mojo. + std::vector<BrokerFilePermission> permissions; + permissions.push_back( + BrokerFilePermission::ReadWriteCreateUnlinkRecursive("/dev/shm/")); + std::unique_ptr<LinuxSandbox> sandbox(new LinuxSandbox(permissions)); + sandbox->Warmup(); + sandbox->EngageNamespaceSandbox(); + sandbox->EngageSeccompSandbox(); + sandbox->Seal(); + return sandbox; +} +#endif + +void RunNativeLibrary(base::NativeLibrary app_library, + mojom::ShellClientRequest shell_client_request) { + if (!RunNativeApplication(app_library, std::move(shell_client_request))) { + LOG(ERROR) << "Failure to RunNativeApplication()"; + } +} + +} // namespace + +int ChildProcessMain() { + DVLOG(2) << "ChildProcessMain()"; + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + +#if defined(OS_LINUX) && !defined(OS_ANDROID) + std::unique_ptr<LinuxSandbox> sandbox; +#endif + base::NativeLibrary app_library = 0; + // Load the application library before we engage the sandbox. + base::FilePath app_library_path = + command_line.GetSwitchValuePath(switches::kChildProcess); + if (!app_library_path.empty()) + app_library = LoadNativeApplication(app_library_path); + base::i18n::InitializeICU(); + if (app_library) + CallLibraryEarlyInitialization(app_library); + +#if defined(OS_MACOSX) + // Send our task port to the parent. + MachBroker::SendTaskPortToParent(); +#endif + +#if !defined(OFFICIAL_BUILD) + // Initialize stack dumping just before initializing sandbox to make + // sure symbol names in all loaded libraries will be cached. + base::debug::EnableInProcessStackDumping(); +#endif +#if defined(OS_LINUX) && !defined(OS_ANDROID) + if (command_line.HasSwitch(switches::kEnableSandbox)) + sandbox = InitializeSandbox(); +#endif + + ChildProcessMain(base::Bind(&RunNativeLibrary, app_library)); + + return 0; +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/child_process.h b/chromium/services/shell/runner/host/child_process.h new file mode 100644 index 00000000000..1f70c007d59 --- /dev/null +++ b/chromium/services/shell/runner/host/child_process.h @@ -0,0 +1,15 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_H_ +#define SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_H_ + +namespace shell { + +// Main method for a child process. +int ChildProcessMain(); + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_H_ diff --git a/chromium/services/shell/runner/host/child_process_base.cc b/chromium/services/shell/runner/host/child_process_base.cc new file mode 100644 index 00000000000..b3daa76bf95 --- /dev/null +++ b/chromium/services/shell/runner/host/child_process_base.cc @@ -0,0 +1,70 @@ +// Copyright 2014 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 "services/shell/runner/host/child_process_base.h" + +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/process_delegate.h" +#include "services/shell/runner/common/client_util.h" + +namespace shell { + +namespace { + +// Should be created and initialized on the main thread and kept alive as long +// a Mojo application is running in the current process. +class ScopedAppContext : public mojo::edk::ProcessDelegate { + public: + ScopedAppContext() + : io_thread_("io_thread"), wait_for_shutdown_event_(true, false) { + // Initialize Mojo before starting any threads. + mojo::edk::Init(); + + // Create and start our I/O thread. + base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0); + CHECK(io_thread_.StartWithOptions(io_thread_options)); + io_runner_ = io_thread_.task_runner().get(); + CHECK(io_runner_.get()); + + mojo::edk::InitIPCSupport(this, io_runner_); + mojo::edk::SetParentPipeHandleFromCommandLine(); + } + + ~ScopedAppContext() override { + mojo::edk::ShutdownIPCSupport(); + wait_for_shutdown_event_.Wait(); + } + + private: + // ProcessDelegate implementation. + void OnShutdownComplete() override { + wait_for_shutdown_event_.Signal(); + } + + base::Thread io_thread_; + scoped_refptr<base::SingleThreadTaskRunner> io_runner_; + + // Used to unblock the main thread on shutdown. + base::WaitableEvent wait_for_shutdown_event_; + + DISALLOW_COPY_AND_ASSIGN(ScopedAppContext); +}; + +} // namespace + +void ChildProcessMain(const RunCallback& callback) { + DCHECK(!base::MessageLoop::current()); + + ScopedAppContext app_context; + callback.Run(GetShellClientRequestFromCommandLine()); +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/child_process_base.h b/chromium/services/shell/runner/host/child_process_base.h new file mode 100644 index 00000000000..367cf88c3db --- /dev/null +++ b/chromium/services/shell/runner/host/child_process_base.h @@ -0,0 +1,21 @@ +// 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. + +#ifndef SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_BASE_H_ +#define SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_BASE_H_ + +#include "base/callback.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" + +namespace shell { + +// Child processes call this to establish the connection to the shell and obtain +// the ShellClientRequest. Once the connection has been established |callback| +// is run. ChildProcessMain() returns once the the callback completes. +using RunCallback = base::Callback<void(mojom::ShellClientRequest)>; +void ChildProcessMain(const RunCallback& callback); + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_BASE_H_ diff --git a/chromium/services/shell/runner/host/child_process_host.cc b/chromium/services/shell/runner/host/child_process_host.cc new file mode 100644 index 00000000000..f9cda1f85fd --- /dev/null +++ b/chromium/services/shell/runner/host/child_process_host.cc @@ -0,0 +1,210 @@ +// Copyright 2014 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 "services/shell/runner/host/child_process_host.h" + +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/process/kill.h" +#include "base/process/launch.h" +#include "base/synchronization/lock.h" +#include "base/task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/public/cpp/bindings/interface_ptr_info.h" +#include "mojo/public/cpp/system/core.h" +#include "services/shell/native_runner_delegate.h" +#include "services/shell/runner/common/client_util.h" +#include "services/shell/runner/common/switches.h" + +#if defined(OS_LINUX) && !defined(OS_ANDROID) +#include "sandbox/linux/services/namespace_sandbox.h" +#endif + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +#if defined(OS_MACOSX) +#include "services/shell/runner/host/mach_broker.h" +#endif + +namespace shell { + +ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, + NativeRunnerDelegate* delegate, + bool start_sandboxed, + const Identity& target, + const base::FilePath& app_path) + : launch_process_runner_(launch_process_runner), + delegate_(delegate), + start_sandboxed_(start_sandboxed), + target_(target), + app_path_(app_path), + start_child_process_event_(false, false), + weak_factory_(this) { +} + +ChildProcessHost::~ChildProcessHost() { + if (!app_path_.empty()) { + CHECK(!mojo_ipc_channel_) + << "Destroying ChildProcessHost before calling Join"; + } +} + +mojom::ShellClientPtr ChildProcessHost::Start( + const Identity& target, + const ProcessReadyCallback& callback, + const base::Closure& quit_closure) { + DCHECK(!child_process_.IsValid()); + + const base::CommandLine* parent_command_line = + base::CommandLine::ForCurrentProcess(); + base::FilePath target_path = parent_command_line->GetProgram(); + // |app_path_| can be empty in tests. + if (!app_path_.MatchesExtension(FILE_PATH_LITERAL(".mojo")) && + !app_path_.empty()) { + target_path = app_path_; + } + + std::unique_ptr<base::CommandLine> child_command_line( + new base::CommandLine(target_path)); + + child_command_line->AppendArguments(*parent_command_line, false); + +#ifndef NDEBUG + child_command_line->AppendSwitchASCII("n", target.name()); + child_command_line->AppendSwitchASCII("u", target.user_id()); +#endif + + if (target_path != app_path_) + child_command_line->AppendSwitchPath(switches::kChildProcess, app_path_); + + if (start_sandboxed_) + child_command_line->AppendSwitch(switches::kEnableSandbox); + + mojo_ipc_channel_.reset(new mojo::edk::PlatformChannelPair); + mojo_ipc_channel_->PrepareToPassClientHandleToChildProcess( + child_command_line.get(), &handle_passing_info_); + + mojom::ShellClientPtr client = + PassShellClientRequestOnCommandLine(child_command_line.get()); + launch_process_runner_->PostTaskAndReply( + FROM_HERE, + base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this), + base::Passed(&child_command_line)), + base::Bind(&ChildProcessHost::DidStart, + weak_factory_.GetWeakPtr(), callback)); + return client; +} + +void ChildProcessHost::Join() { + if (mojo_ipc_channel_) + start_child_process_event_.Wait(); + mojo_ipc_channel_.reset(); + if (child_process_.IsValid()) { + int rv = -1; + LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) + << "Failed to wait for child process"; + child_process_.Close(); + } +} + +void ChildProcessHost::DidStart(const ProcessReadyCallback& callback) { + if (child_process_.IsValid()) { + callback.Run(child_process_.Pid()); + } else { + LOG(ERROR) << "Failed to start child process"; + mojo_ipc_channel_.reset(); + callback.Run(base::kNullProcessId); + } +} + +void ChildProcessHost::DoLaunch( + std::unique_ptr<base::CommandLine> child_command_line) { + if (delegate_) { + delegate_->AdjustCommandLineArgumentsForTarget(target_, + child_command_line.get()); + } + + base::LaunchOptions options; +#if defined(OS_WIN) + options.handles_to_inherit = &handle_passing_info_; +#if defined(OFFICIAL_BUILD) + CHECK(false) << "Launching mojo process with inherit_handles is insecure!"; +#endif + options.inherit_handles = true; + options.stdin_handle = INVALID_HANDLE_VALUE; + options.stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + options.stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + // Always inherit stdout/stderr as a pair. + if (!options.stdout_handle || !options.stdin_handle) + options.stdin_handle = options.stdout_handle = nullptr; + + // Pseudo handles are used when stdout and stderr redirect to the console. In + // that case, they're automatically inherited by child processes. See + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075.aspx + // Trying to add them to the list of handles to inherit causes CreateProcess + // to fail. When this process is launched from Python then a real handle is + // used. In that case, we do want to add it to the list of handles that is + // inherited. + if (options.stdout_handle && + GetFileType(options.stdout_handle) != FILE_TYPE_CHAR) { + handle_passing_info_.push_back(options.stdout_handle); + } + if (options.stderr_handle && + GetFileType(options.stderr_handle) != FILE_TYPE_CHAR && + options.stdout_handle != options.stderr_handle) { + handle_passing_info_.push_back(options.stderr_handle); + } +#elif defined(OS_POSIX) + handle_passing_info_.push_back(std::make_pair(STDIN_FILENO, STDIN_FILENO)); + handle_passing_info_.push_back(std::make_pair(STDOUT_FILENO, STDOUT_FILENO)); + handle_passing_info_.push_back(std::make_pair(STDERR_FILENO, STDERR_FILENO)); + options.fds_to_remap = &handle_passing_info_; +#endif + DVLOG(2) << "Launching child with command line: " + << child_command_line->GetCommandLineString(); +#if defined(OS_LINUX) && !defined(OS_ANDROID) + if (start_sandboxed_) { + child_process_ = + sandbox::NamespaceSandbox::LaunchProcess(*child_command_line, options); + if (!child_process_.IsValid()) { + LOG(ERROR) << "Starting the process with a sandbox failed. Missing kernel" + << " support."; + } + } else +#endif + { +#if defined(OS_MACOSX) + MachBroker* mach_broker = MachBroker::GetInstance(); + base::AutoLock locker(mach_broker->GetLock()); +#endif + child_process_ = base::LaunchProcess(*child_command_line, options); +#if defined(OS_MACOSX) + mach_broker->ExpectPid(child_process_.Handle()); +#endif + } + + if (child_process_.IsValid()) { + if (mojo_ipc_channel_.get()) { + mojo_ipc_channel_->ChildProcessLaunched(); + mojo::edk::ChildProcessLaunched( + child_process_.Handle(), + mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle( + mojo_ipc_channel_->PassServerHandle().release().handle))); + } + } + start_child_process_event_.Signal(); +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/child_process_host.h b/chromium/services/shell/runner/host/child_process_host.h new file mode 100644 index 00000000000..3234cd90cbe --- /dev/null +++ b/chromium/services/shell/runner/host/child_process_host.h @@ -0,0 +1,98 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_HOST_H_ +#define SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_HOST_H_ + +#include <stdint.h> + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/process/process.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "mojo/edk/embedder/platform_channel_pair.h" +#include "mojo/public/cpp/system/message_pipe.h" +#include "services/shell/public/cpp/identity.h" +#include "services/shell/public/interfaces/shell_client_factory.mojom.h" +#include "services/shell/runner/host/child_process_host.h" + +namespace base { +class TaskRunner; +} + +namespace shell { + +class Identity; +class NativeRunnerDelegate; + +// This class represents a "child process host". Handles launching and +// connecting a platform-specific "pipe" to the child, and supports joining the +// child process. Currently runs a single app (loaded from the file system). +// +// This class is not thread-safe. It should be created/used/destroyed on a +// single thread. +// +// Note: Does not currently work on Windows before Vista. +// Note: After |Start()|, |StartApp| must be called and this object must +// remained alive until the |on_app_complete| callback is called. +class ChildProcessHost { + public: + using ProcessReadyCallback = base::Callback<void(base::ProcessId)>; + + // |name| is just for debugging ease. We will spawn off a process so that it + // can be sandboxed if |start_sandboxed| is true. |app_path| is a path to the + // mojo application we wish to start. + ChildProcessHost(base::TaskRunner* launch_process_runner, + NativeRunnerDelegate* delegate, + bool start_sandboxed, + const Identity& target, + const base::FilePath& app_path); + virtual ~ChildProcessHost(); + + // |Start()|s the child process; calls |DidStart()| (on the thread on which + // |Start()| was called) when the child has been started (or failed to start). + mojom::ShellClientPtr Start(const Identity& target, + const ProcessReadyCallback& callback, + const base::Closure& quit_closure); + + // Waits for the child process to terminate. + void Join(); + + protected: + void DidStart(const ProcessReadyCallback& callback); + + private: + void DoLaunch(std::unique_ptr<base::CommandLine> child_command_line); + + scoped_refptr<base::TaskRunner> launch_process_runner_; + NativeRunnerDelegate* delegate_ = nullptr; + bool start_sandboxed_ = false; + Identity target_; + const base::FilePath app_path_; + base::Process child_process_; + + // Used to initialize the Mojo IPC channel between parent and child. + std::unique_ptr<mojo::edk::PlatformChannelPair> mojo_ipc_channel_; + mojo::edk::HandlePassingInformation handle_passing_info_; + + // Since Start() calls a method on another thread, we use an event to block + // the main thread if it tries to destruct |this| while launching the process. + base::WaitableEvent start_child_process_event_; + + base::WeakPtrFactory<ChildProcessHost> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ChildProcessHost); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_HOST_CHILD_PROCESS_HOST_H_ diff --git a/chromium/services/shell/runner/host/child_process_host_unittest.cc b/chromium/services/shell/runner/host/child_process_host_unittest.cc new file mode 100644 index 00000000000..f4867d4e754 --- /dev/null +++ b/chromium/services/shell/runner/host/child_process_host_unittest.cc @@ -0,0 +1,109 @@ +// Copyright 2014 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. + +// Note: This file also tests child_process.*. + +#include "services/shell/runner/host/child_process_host.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/threading/thread.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/process_delegate.h" +#include "services/shell/native_runner_delegate.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace shell { +namespace { + +void ProcessReadyCallbackAdapater(const base::Closure& callback, + base::ProcessId process_id) { + callback.Run(); +} + +class ProcessDelegate : public mojo::edk::ProcessDelegate { + public: + ProcessDelegate() {} + ~ProcessDelegate() override {} + + private: + void OnShutdownComplete() override {} + DISALLOW_COPY_AND_ASSIGN(ProcessDelegate); +}; + +class NativeRunnerDelegateImpl : public NativeRunnerDelegate { + public: + NativeRunnerDelegateImpl() {} + ~NativeRunnerDelegateImpl() override {} + + size_t get_and_clear_adjust_count() { + size_t count = 0; + std::swap(count, adjust_count_); + return count; + } + + private: + // NativeRunnerDelegate: + void AdjustCommandLineArgumentsForTarget( + const Identity& target, + base::CommandLine* command_line) override { + adjust_count_++; + } + + size_t adjust_count_ = 0; + + DISALLOW_COPY_AND_ASSIGN(NativeRunnerDelegateImpl); +}; + +#if defined(OS_ANDROID) +// TODO(qsr): Multiprocess shell tests are not supported on android. +#define MAYBE_StartJoin DISABLED_StartJoin +#else +#define MAYBE_StartJoin StartJoin +#endif // defined(OS_ANDROID) +// Just tests starting the child process and joining it (without starting an +// app). +TEST(ChildProcessHostTest, MAYBE_StartJoin) { + base::FilePath shell_dir; + PathService::Get(base::DIR_MODULE, &shell_dir); + base::MessageLoop message_loop; + scoped_refptr<base::SequencedWorkerPool> blocking_pool( + new base::SequencedWorkerPool(3, "blocking_pool")); + + base::Thread io_thread("io_thread"); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + io_thread.StartWithOptions(options); + + ProcessDelegate delegate; + mojo::edk::InitIPCSupport(&delegate, io_thread.task_runner()); + + NativeRunnerDelegateImpl native_runner_delegate; + ChildProcessHost child_process_host(blocking_pool.get(), + &native_runner_delegate, false, + Identity(), base::FilePath()); + base::RunLoop run_loop; + child_process_host.Start( + Identity(), + base::Bind(&ProcessReadyCallbackAdapater, run_loop.QuitClosure()), + base::Bind(&base::DoNothing)); + run_loop.Run(); + + child_process_host.Join(); + blocking_pool->Shutdown(); + mojo::edk::ShutdownIPCSupport(); + EXPECT_EQ(1u, native_runner_delegate.get_and_clear_adjust_count()); +} + +} // namespace +} // namespace shell diff --git a/chromium/services/shell/runner/host/host_unittests.cc b/chromium/services/shell/runner/host/host_unittests.cc new file mode 100644 index 00000000000..37192107ce9 --- /dev/null +++ b/chromium/services/shell/runner/host/host_unittests.cc @@ -0,0 +1,36 @@ +// Copyright 2014 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 "base/at_exit.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" +#include "mojo/edk/embedder/embedder.h" +#include "services/shell/runner/common/switches.h" +#include "services/shell/runner/host/child_process.h" +#include "services/shell/runner/init.h" +#include "testing/gtest/include/gtest/gtest.h" + +int main(int argc, char** argv) { + base::CommandLine::Init(argc, argv); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + shell::WaitForDebuggerIfNecessary(); + + if (command_line.HasSwitch(switches::kChildProcess)) { + base::AtExitManager at_exit; + + return shell::ChildProcessMain(); + } + + mojo::edk::Init(); + + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, argv, + base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); +} diff --git a/chromium/services/shell/runner/host/in_process_native_runner.cc b/chromium/services/shell/runner/host/in_process_native_runner.cc new file mode 100644 index 00000000000..e18cfa6a2fc --- /dev/null +++ b/chromium/services/shell/runner/host/in_process_native_runner.cc @@ -0,0 +1,95 @@ +// Copyright 2014 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 "services/shell/runner/host/in_process_native_runner.h" + +#include <utility> + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/memory/ptr_util.h" +#include "base/process/process.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task_runner.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread_task_runner_handle.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/shell/runner/host/native_application_support.h" +#include "services/shell/runner/host/out_of_process_native_runner.h" +#include "services/shell/runner/init.h" + +namespace shell { + +InProcessNativeRunner::InProcessNativeRunner() : app_library_(nullptr) {} + +InProcessNativeRunner::~InProcessNativeRunner() { + // It is important to let the thread exit before unloading the DSO (when + // app_library_ is destructed), because the library may have registered + // thread-local data and destructors to run on thread termination. + if (thread_) { + DCHECK(thread_->HasBeenStarted()); + DCHECK(!thread_->HasBeenJoined()); + thread_->Join(); + } +} + +mojom::ShellClientPtr InProcessNativeRunner::Start( + const base::FilePath& app_path, + const Identity& target, + bool start_sandboxed, + const base::Callback<void(base::ProcessId)>& pid_available_callback, + const base::Closure& app_completed_callback) { + app_path_ = app_path; + + DCHECK(!request_.is_pending()); + mojom::ShellClientPtr client; + request_ = GetProxy(&client); + + DCHECK(app_completed_callback_runner_.is_null()); + app_completed_callback_runner_ = base::Bind( + &base::TaskRunner::PostTask, base::ThreadTaskRunnerHandle::Get(), + FROM_HERE, app_completed_callback); + + DCHECK(!thread_); + std::string thread_name = "mojo:app_thread"; +#if defined(OS_WIN) + thread_name = base::WideToUTF8(app_path_.BaseName().value()); +#endif + thread_.reset(new base::DelegateSimpleThread(this, thread_name)); + thread_->Start(); + pid_available_callback.Run(base::Process::Current().Pid()); + + return client; +} + +void InProcessNativeRunner::Run() { + DVLOG(2) << "Loading/running Mojo app in process from library: " + << app_path_.value() + << " thread id=" << base::PlatformThread::CurrentId(); + + // TODO(vtl): ScopedNativeLibrary doesn't have a .get() method! + base::NativeLibrary app_library = LoadNativeApplication(app_path_); + app_library_.Reset(app_library); + // This hangs on Windows in the component build, so skip it since it's + // unnecessary. +#if !(defined(COMPONENT_BUILD) && defined(OS_WIN)) + CallLibraryEarlyInitialization(app_library); +#endif + RunNativeApplication(app_library, std::move(request_)); + app_completed_callback_runner_.Run(); + app_completed_callback_runner_.Reset(); +} + +std::unique_ptr<NativeRunner> InProcessNativeRunnerFactory::Create( + const base::FilePath& app_path) { + // Non-Mojo apps are always run in a new process. + if (!app_path.MatchesExtension(FILE_PATH_LITERAL(".mojo"))) { + return base::WrapUnique( + new OutOfProcessNativeRunner(launch_process_runner_, nullptr)); + } + return base::WrapUnique(new InProcessNativeRunner); +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/in_process_native_runner.h b/chromium/services/shell/runner/host/in_process_native_runner.h new file mode 100644 index 00000000000..8b1af2636b0 --- /dev/null +++ b/chromium/services/shell/runner/host/in_process_native_runner.h @@ -0,0 +1,70 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_RUNNER_HOST_IN_PROCESS_NATIVE_RUNNER_H_ +#define SERVICES_SHELL_RUNNER_HOST_IN_PROCESS_NATIVE_RUNNER_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/scoped_native_library.h" +#include "base/threading/simple_thread.h" +#include "services/shell/native_runner.h" +#include "services/shell/runner/host/native_application_support.h" + +namespace base { +class TaskRunner; +} + +namespace shell { + +// An implementation of |NativeRunner| that loads/runs the given app (from the +// file system) on a separate thread (in the current process). +class InProcessNativeRunner : public NativeRunner, + public base::DelegateSimpleThread::Delegate { + public: + InProcessNativeRunner(); + ~InProcessNativeRunner() override; + + // NativeRunner: + mojom::ShellClientPtr Start( + const base::FilePath& app_path, + const Identity& target, + bool start_sandboxed, + const base::Callback<void(base::ProcessId)>& pid_available_callback, + const base::Closure& app_completed_callback) override; + + private: + // |base::DelegateSimpleThread::Delegate| method: + void Run() override; + + base::FilePath app_path_; + mojom::ShellClientRequest request_; + base::Callback<bool(void)> app_completed_callback_runner_; + + base::ScopedNativeLibrary app_library_; + std::unique_ptr<base::DelegateSimpleThread> thread_; + + DISALLOW_COPY_AND_ASSIGN(InProcessNativeRunner); +}; + +class InProcessNativeRunnerFactory : public NativeRunnerFactory { + public: + explicit InProcessNativeRunnerFactory(base::TaskRunner* launch_process_runner) + : launch_process_runner_(launch_process_runner) {} + ~InProcessNativeRunnerFactory() override {} + + std::unique_ptr<NativeRunner> Create(const base::FilePath& app_path) override; + + private: + base::TaskRunner* const launch_process_runner_; + + DISALLOW_COPY_AND_ASSIGN(InProcessNativeRunnerFactory); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_HOST_IN_PROCESS_NATIVE_RUNNER_H_ diff --git a/chromium/services/shell/runner/host/in_process_native_runner_unittest.cc b/chromium/services/shell/runner/host/in_process_native_runner_unittest.cc new file mode 100644 index 00000000000..8f679679497 --- /dev/null +++ b/chromium/services/shell/runner/host/in_process_native_runner_unittest.cc @@ -0,0 +1,16 @@ +// Copyright 2014 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 "services/shell/runner/host/in_process_native_runner.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace shell { + +TEST(InProcessNativeRunnerTest, NotStarted) { + InProcessNativeRunner runner; + // Shouldn't crash or DCHECK on destruction. +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/linux_sandbox.cc b/chromium/services/shell/runner/host/linux_sandbox.cc new file mode 100644 index 00000000000..24461a5a645 --- /dev/null +++ b/chromium/services/shell/runner/host/linux_sandbox.cc @@ -0,0 +1,167 @@ +// 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 "services/shell/runner/host/linux_sandbox.h" + +#include <fcntl.h> +#include <sys/syscall.h> +#include <utility> + +#include "base/bind.h" +#include "base/debug/leak_annotations.h" +#include "base/macros.h" +#include "base/posix/eintr_wrapper.h" +#include "base/rand_util.h" +#include "base/sys_info.h" +#include "sandbox/linux/bpf_dsl/policy.h" +#include "sandbox/linux/bpf_dsl/trap_registry.h" +#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" +#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" +#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" +#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/services/credentials.h" +#include "sandbox/linux/services/namespace_sandbox.h" +#include "sandbox/linux/services/proc_util.h" +#include "sandbox/linux/services/thread_helpers.h" + +using sandbox::syscall_broker::BrokerFilePermission; + +namespace shell { + +namespace { + +intptr_t SandboxSIGSYSHandler(const struct sandbox::arch_seccomp_data& args, + void* aux) { + RAW_CHECK(aux); + const sandbox::syscall_broker::BrokerProcess* broker_process = + static_cast<const sandbox::syscall_broker::BrokerProcess*>(aux); + switch (args.nr) { + case __NR_access: + return broker_process->Access(reinterpret_cast<const char*>(args.args[0]), + static_cast<int>(args.args[1])); + case __NR_open: + return broker_process->Open(reinterpret_cast<const char*>(args.args[0]), + static_cast<int>(args.args[1])); + case __NR_faccessat: + if (static_cast<int>(args.args[0]) == AT_FDCWD) { + return broker_process->Access( + reinterpret_cast<const char*>(args.args[1]), + static_cast<int>(args.args[2])); + } else { + return -EPERM; + } + case __NR_openat: + // Allow using openat() as open(). + if (static_cast<int>(args.args[0]) == AT_FDCWD) { + return broker_process->Open(reinterpret_cast<const char*>(args.args[1]), + static_cast<int>(args.args[2])); + } else { + return -EPERM; + } + default: + RAW_CHECK(false); + return -ENOSYS; + } +} + +class SandboxPolicy : public sandbox::BaselinePolicy { + public: + explicit SandboxPolicy(sandbox::syscall_broker::BrokerProcess* broker_process) + : broker_process_(broker_process) {} + ~SandboxPolicy() override {} + + // Overridden from sandbox::bpf_dsl::Policy: + sandbox::bpf_dsl::ResultExpr EvaluateSyscall(int sysno) const override { + // This policy is only advisory/for noticing FS access for the moment. + switch (sysno) { +#if !defined(__aarch64__) + case __NR_access: + case __NR_open: +#endif + case __NR_faccessat: + case __NR_openat: + return sandbox::bpf_dsl::Trap(SandboxSIGSYSHandler, broker_process_); + case __NR_sched_getaffinity: + return sandbox::RestrictSchedTarget(policy_pid(), sysno); + case __NR_ftruncate: +#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ + defined(__aarch64__) + // Per #ifdefs in + // content/common/sandbox_linux/bpf_renderer_policy_linux.cc + case __NR_getrlimit: +#endif +#if defined(__i386__) || defined(__arm__) + case __NR_ugetrlimit: +#endif + case __NR_uname: +#if defined(__arm__) || defined(__x86_64__) || defined(__mips__) + case __NR_getsockopt: + case __NR_setsockopt: +#endif + return sandbox::bpf_dsl::Allow(); + } + + return BaselinePolicy::EvaluateSyscall(sysno); + } + + private: + // Not owned. + const sandbox::syscall_broker::BrokerProcess* broker_process_; + DISALLOW_COPY_AND_ASSIGN(SandboxPolicy); +}; + +} // namespace + +LinuxSandbox::LinuxSandbox(const std::vector<BrokerFilePermission>& permissions) + : broker_(new sandbox::syscall_broker::BrokerProcess(EPERM, permissions)) { + CHECK(broker_->Init( + base::Bind<bool (*)()>(&sandbox::Credentials::DropAllCapabilities))); + policy_.reset(new SandboxPolicy(broker_.get())); +} + +LinuxSandbox::~LinuxSandbox() {} + +void LinuxSandbox::Warmup() { + proc_fd_ = sandbox::ProcUtil::OpenProc(); + warmed_up_ = true; + + // Verify that we haven't started threads or grabbed directory file + // descriptors. + sandbox::ThreadHelpers::AssertSingleThreaded(proc_fd_.get()); + CHECK(!sandbox::ProcUtil::HasOpenDirectory(proc_fd_.get())); +} + +void LinuxSandbox::EngageNamespaceSandbox() { + CHECK(warmed_up_); + CHECK_EQ(1, getpid()); + CHECK(sandbox::NamespaceSandbox::InNewPidNamespace()); + CHECK(sandbox::Credentials::MoveToNewUserNS()); + CHECK(sandbox::Credentials::DropFileSystemAccess(proc_fd_.get())); + CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd_.get())); +} + +void LinuxSandbox::EngageSeccompSandbox() { + CHECK(warmed_up_); + sandbox::SandboxBPF sandbox(policy_.release()); + base::ScopedFD proc_fd(HANDLE_EINTR( + openat(proc_fd_.get(), ".", O_RDONLY | O_DIRECTORY | O_CLOEXEC))); + CHECK(proc_fd.is_valid()); + sandbox.SetProcFd(std::move(proc_fd)); + CHECK( + sandbox.StartSandbox(sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED)) + << "Starting the process with a sandbox failed. Missing kernel support."; + + // The Broker is now bound to this process and should only be destroyed when + // the process exits or is killed. + sandbox::syscall_broker::BrokerProcess* leaked_broker = broker_.release(); + ALLOW_UNUSED_LOCAL(leaked_broker); + ANNOTATE_LEAKING_OBJECT_PTR(leaked_broker); +} + +void LinuxSandbox::Seal() { + proc_fd_.reset(); +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/linux_sandbox.h b/chromium/services/shell/runner/host/linux_sandbox.h new file mode 100644 index 00000000000..bd1880a54c5 --- /dev/null +++ b/chromium/services/shell/runner/host/linux_sandbox.h @@ -0,0 +1,51 @@ +// 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. + +#ifndef SERVICES_SHELL_RUNNER_HOST_LINUX_SANDBOX_H_ +#define SERVICES_SHELL_RUNNER_HOST_LINUX_SANDBOX_H_ + +#include <memory> + +#include "base/files/scoped_file.h" +#include "base/macros.h" +#include "sandbox/linux/bpf_dsl/bpf_dsl.h" +#include "sandbox/linux/bpf_dsl/policy.h" +#include "sandbox/linux/syscall_broker/broker_process.h" + +namespace shell { + +// Encapsulates all tasks related to raising the sandbox for mojo runner. +class LinuxSandbox { + public: + explicit LinuxSandbox( + const std::vector<sandbox::syscall_broker::BrokerFilePermission>& + permissions); + ~LinuxSandbox(); + + // Grabs a file descriptor to /proc. + void Warmup(); + + // Puts the user in a new PID namespace. + void EngageNamespaceSandbox(); + + // Starts a broker process and sets up seccomp-bpf to delegate decisions to + // it. + void EngageSeccompSandbox(); + + // Performs the dropping of access to the outside world (drops the reference + // to /proc acquired in Warmup(). + void Seal(); + + private: + bool warmed_up_; + base::ScopedFD proc_fd_; + std::unique_ptr<sandbox::syscall_broker::BrokerProcess> broker_; + std::unique_ptr<sandbox::bpf_dsl::Policy> policy_; + + DISALLOW_COPY_AND_ASSIGN(LinuxSandbox); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_HOST_LINUX_SANDBOX_H_ diff --git a/chromium/services/shell/runner/host/mach_broker.cc b/chromium/services/shell/runner/host/mach_broker.cc new file mode 100644 index 00000000000..7f1cd45fa0d --- /dev/null +++ b/chromium/services/shell/runner/host/mach_broker.cc @@ -0,0 +1,43 @@ +// 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 "services/shell/runner/host/mach_broker.h" + +#include "base/logging.h" +#include "base/memory/singleton.h" + +namespace shell { + +namespace { +const char kBootstrapPortName[] = "mojo_shell"; +} + +// static +void MachBroker::SendTaskPortToParent() { + bool result = base::MachPortBroker::ChildSendTaskPortToParent( + kBootstrapPortName); + DCHECK(result); +} + +// static +MachBroker* MachBroker::GetInstance() { + return base::Singleton<MachBroker>::get(); +} + +MachBroker::MachBroker() : broker_(kBootstrapPortName) { + bool result = broker_.Init(); + DCHECK(result); +} + +MachBroker::~MachBroker() {} + +void MachBroker::ExpectPid(base::ProcessHandle pid) { + broker_.AddPlaceholderForPid(pid); +} + +void MachBroker::RemovePid(base::ProcessHandle pid) { + broker_.InvalidatePid(pid); +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/mach_broker.h b/chromium/services/shell/runner/host/mach_broker.h new file mode 100644 index 00000000000..64fa63a6cc7 --- /dev/null +++ b/chromium/services/shell/runner/host/mach_broker.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef SERVICES_SHELL_RUNNER_HOST_MACH_BROKER_H_ +#define SERVICES_SHELL_RUNNER_HOST_MACH_BROKER_H_ + +#include "base/mac/mach_port_broker.h" + +namespace base { +template <typename T> struct DefaultSingletonTraits; +} + +namespace shell { + +// A global singleton |MachBroker| is used by the shell to provide access to +// Mach task ports for shell out-of-process applications. +class MachBroker { + public: + // Sends the task port of the current process to the parent over Mach IPC. + // For use in child processes. + static void SendTaskPortToParent(); + + // Returns the global |MachBroker|. For use in the shell. + static MachBroker* GetInstance(); + + // Registers |pid| with a MACH_PORT_NULL task port in the port provider. A + // child's pid must be registered before the broker will accept a task port + // from that child. + // Callers MUST acquire the lock given by GetLock() before calling this method + // (and release the lock afterwards). + void ExpectPid(base::ProcessHandle pid); + + // Removes |pid| from the port provider. + // Callers MUST acquire the lock given by GetLock() before calling this method + // (and release the lock afterwards). + void RemovePid(base::ProcessHandle pid); + + base::Lock& GetLock() { return broker_.GetLock(); } + base::PortProvider* port_provider() { return &broker_; } + + private: + MachBroker(); + ~MachBroker(); + friend struct base::DefaultSingletonTraits<MachBroker>; + + base::MachPortBroker broker_; +}; + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_HOST_MACH_BROKER_H_ diff --git a/chromium/services/shell/runner/host/native_application_support.cc b/chromium/services/shell/runner/host/native_application_support.cc new file mode 100644 index 00000000000..cb213748cf9 --- /dev/null +++ b/chromium/services/shell/runner/host/native_application_support.cc @@ -0,0 +1,106 @@ +// Copyright 2014 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 "services/shell/runner/host/native_application_support.h" + +#include <stddef.h> + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "mojo/platform_handle/platform_handle_private_thunks.h" +#include "mojo/public/platform/native/system_thunks.h" + +namespace shell { + +namespace { + +template <typename Thunks> +bool SetThunks(Thunks (*make_thunks)(), + const char* function_name, + base::NativeLibrary library) { + typedef size_t (*SetThunksFn)(const Thunks* thunks); + SetThunksFn set_thunks = reinterpret_cast<SetThunksFn>( + base::GetFunctionPointerFromNativeLibrary(library, function_name)); + if (!set_thunks) + return false; + Thunks thunks = make_thunks(); + size_t expected_size = set_thunks(&thunks); + if (expected_size > sizeof(Thunks)) { + LOG(ERROR) << "Invalid app library: expected " << function_name + << " to return thunks of size: " << expected_size; + return false; + } + return true; +} + +} // namespace + +base::NativeLibrary LoadNativeApplication(const base::FilePath& app_path) { + DVLOG(2) << "Loading Mojo app in process from library: " << app_path.value(); + + base::NativeLibraryLoadError error; + base::NativeLibrary app_library = base::LoadNativeLibrary(app_path, &error); + LOG_IF(ERROR, !app_library) + << "Failed to load app library (path: " << app_path.value() << ")"; + return app_library; +} + +bool RunNativeApplication(base::NativeLibrary app_library, + mojom::ShellClientRequest request) { + // Tolerate |app_library| being null, to make life easier for callers. + if (!app_library) + return false; + +// Thunks aren't needed/used in component build, since the thunked methods +// just live in their own dynamically loaded library. +#if !defined(COMPONENT_BUILD) + if (!SetThunks(&MojoMakeSystemThunks, "MojoSetSystemThunks", app_library)) { + LOG(ERROR) << "MojoSetSystemThunks not found"; + return false; + } + +#if !defined(OS_WIN) + // On Windows, initializing base::CommandLine with null parameters gets the + // process's command line from the OS. Other platforms need it to be passed + // in. This needs to be passed in before the app initializes the command line, + // which is done as soon as it loads. + typedef void (*InitCommandLineArgs)(int, const char* const*); + InitCommandLineArgs init_command_line_args = + reinterpret_cast<InitCommandLineArgs>( + base::GetFunctionPointerFromNativeLibrary(app_library, + "InitCommandLineArgs")); + if (init_command_line_args) { + int argc = 0; + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + const char** argv = new const char*[cmd_line->argv().size()]; + for (auto& arg : cmd_line->argv()) + argv[argc++] = arg.c_str(); + init_command_line_args(argc, argv); + } +#endif + + // Apps need not include platform handle thunks. + SetThunks(&MojoMakePlatformHandlePrivateThunks, + "MojoSetPlatformHandlePrivateThunks", app_library); +#endif + + typedef MojoResult (*MojoMainFunction)(MojoHandle); + MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>( + base::GetFunctionPointerFromNativeLibrary(app_library, "MojoMain")); + if (!main_function) { + LOG(ERROR) << "MojoMain not found"; + return false; + } + // |MojoMain()| takes ownership of the service handle. + MojoHandle handle = request.PassMessagePipe().release().value(); + MojoResult result = main_function(handle); + if (result != MOJO_RESULT_OK) { + LOG(ERROR) << "MojoMain returned error (result: " << result << ")"; + } + return true; +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/native_application_support.h b/chromium/services/shell/runner/host/native_application_support.h new file mode 100644 index 00000000000..2f8df6997bc --- /dev/null +++ b/chromium/services/shell/runner/host/native_application_support.h @@ -0,0 +1,38 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_RUNNER_HOST_NATIVE_APPLICATION_SUPPORT_H_ +#define SERVICES_SHELL_RUNNER_HOST_NATIVE_APPLICATION_SUPPORT_H_ + +#include "base/native_library.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" + +namespace base { +class FilePath; +} + +namespace shell { + +// Loads the native Mojo application from the DSO specified by |app_path|. +// Returns the |base::NativeLibrary| for the application on success (or null on +// failure). +// +// Note: The caller may choose to eventually unload the returned DSO. If so, +// this should be done only after the thread on which |LoadNativeApplication()| +// and |RunNativeApplication()| were called has terminated, so that any +// thread-local destructors have been executed. +base::NativeLibrary LoadNativeApplication(const base::FilePath& app_path); + +// Runs the native Mojo application from the DSO that was loaded using +// |LoadNativeApplication()|; this tolerates |app_library| being null. This +// should be called on the same thread as |LoadNativeApplication()|. Returns +// true if |MojoMain()| was called (even if it returns an error), and false +// otherwise. +bool RunNativeApplication(base::NativeLibrary app_library, + mojom::ShellClientRequest request); + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_HOST_NATIVE_APPLICATION_SUPPORT_H_ diff --git a/chromium/services/shell/runner/host/out_of_process_native_runner.cc b/chromium/services/shell/runner/host/out_of_process_native_runner.cc new file mode 100644 index 00000000000..eeeb234256b --- /dev/null +++ b/chromium/services/shell/runner/host/out_of_process_native_runner.cc @@ -0,0 +1,75 @@ +// Copyright 2014 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 "services/shell/runner/host/out_of_process_native_runner.h" + +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/task_runner.h" +#include "services/shell/runner/common/client_util.h" +#include "services/shell/runner/host/child_process_host.h" +#include "services/shell/runner/host/in_process_native_runner.h" + +namespace shell { + +OutOfProcessNativeRunner::OutOfProcessNativeRunner( + base::TaskRunner* launch_process_runner, + NativeRunnerDelegate* delegate) + : launch_process_runner_(launch_process_runner), delegate_(delegate) {} + +OutOfProcessNativeRunner::~OutOfProcessNativeRunner() { + if (child_process_host_ && !app_path_.empty()) + child_process_host_->Join(); +} + +mojom::ShellClientPtr OutOfProcessNativeRunner::Start( + const base::FilePath& app_path, + const Identity& target, + bool start_sandboxed, + const base::Callback<void(base::ProcessId)>& pid_available_callback, + const base::Closure& app_completed_callback) { + app_path_ = app_path; + + DCHECK(app_completed_callback_.is_null()); + app_completed_callback_ = app_completed_callback; + + child_process_host_.reset(new ChildProcessHost( + launch_process_runner_, delegate_, start_sandboxed, target, app_path)); + return child_process_host_->Start( + target, pid_available_callback, + base::Bind(&OutOfProcessNativeRunner::AppCompleted, + base::Unretained(this))); +} + +void OutOfProcessNativeRunner::AppCompleted() { + if (child_process_host_) + child_process_host_->Join(); + child_process_host_.reset(); + // This object may be deleted by this callback. + base::Closure app_completed_callback = app_completed_callback_; + app_completed_callback_.Reset(); + if (!app_completed_callback.is_null()) + app_completed_callback.Run(); +} + +OutOfProcessNativeRunnerFactory::OutOfProcessNativeRunnerFactory( + base::TaskRunner* launch_process_runner, + NativeRunnerDelegate* delegate) + : launch_process_runner_(launch_process_runner), delegate_(delegate) {} +OutOfProcessNativeRunnerFactory::~OutOfProcessNativeRunnerFactory() {} + +std::unique_ptr<NativeRunner> OutOfProcessNativeRunnerFactory::Create( + const base::FilePath& app_path) { + return base::WrapUnique( + new OutOfProcessNativeRunner(launch_process_runner_, delegate_)); +} + +} // namespace shell diff --git a/chromium/services/shell/runner/host/out_of_process_native_runner.h b/chromium/services/shell/runner/host/out_of_process_native_runner.h new file mode 100644 index 00000000000..d141c90e0c3 --- /dev/null +++ b/chromium/services/shell/runner/host/out_of_process_native_runner.h @@ -0,0 +1,74 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_RUNNER_HOST_OUT_OF_PROCESS_NATIVE_RUNNER_H_ +#define SERVICES_SHELL_RUNNER_HOST_OUT_OF_PROCESS_NATIVE_RUNNER_H_ + +#include <stdint.h> + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "services/shell/native_runner.h" + +namespace base { +class TaskRunner; +} + +namespace shell { + +class ChildProcessHost; +class NativeRunnerDelegate; + +// An implementation of |NativeRunner| that loads/runs the given app (from the +// file system) in a separate process (of its own). +class OutOfProcessNativeRunner : public NativeRunner { + public: + OutOfProcessNativeRunner(base::TaskRunner* launch_process_runner, + NativeRunnerDelegate* delegate); + ~OutOfProcessNativeRunner() override; + + // NativeRunner: + mojom::ShellClientPtr Start( + const base::FilePath& app_path, + const Identity& identity, + bool start_sandboxed, + const base::Callback<void(base::ProcessId)>& pid_available_callback, + const base::Closure& app_completed_callback) override; + + private: + void AppCompleted(); + + base::TaskRunner* const launch_process_runner_; + NativeRunnerDelegate* delegate_; + + base::FilePath app_path_; + base::Closure app_completed_callback_; + + std::unique_ptr<ChildProcessHost> child_process_host_; + + DISALLOW_COPY_AND_ASSIGN(OutOfProcessNativeRunner); +}; + +class OutOfProcessNativeRunnerFactory : public NativeRunnerFactory { + public: + OutOfProcessNativeRunnerFactory(base::TaskRunner* launch_process_runner, + NativeRunnerDelegate* delegate); + ~OutOfProcessNativeRunnerFactory() override; + + std::unique_ptr<NativeRunner> Create(const base::FilePath& app_path) override; + + private: + base::TaskRunner* const launch_process_runner_; + NativeRunnerDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(OutOfProcessNativeRunnerFactory); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_HOST_OUT_OF_PROCESS_NATIVE_RUNNER_H_ diff --git a/chromium/services/shell/runner/init.cc b/chromium/services/shell/runner/init.cc new file mode 100644 index 00000000000..8d095f0f2fc --- /dev/null +++ b/chromium/services/shell/runner/init.cc @@ -0,0 +1,98 @@ +// Copyright 2013 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 "services/shell/runner/init.h" + +#include <stdint.h> + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug/debugger.h" +#include "base/files/file_path.h" +#include "base/i18n/icu_util.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/strings/string_split.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "services/shell/runner/common/switches.h" + +#if defined(OS_WIN) +#include <windows.h> +#elif (OS_POSIX) +#include <unistd.h> +#endif + +namespace shell { + +void InitializeLogging() { + logging::LoggingSettings settings; + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + logging::InitLogging(settings); + // To view log output with IDs and timestamps use "adb logcat -v threadtime". + logging::SetLogItems(true, // Process ID + true, // Thread ID + true, // Timestamp + true); // Tick count +} + +void WaitForDebuggerIfNecessary() { + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kWaitForDebugger)) { + std::vector<std::string> apps_to_debug = base::SplitString( + command_line->GetSwitchValueASCII(switches::kWaitForDebugger), ",", + base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + std::string app = "launcher"; + if (command_line->HasSwitch(switches::kChildProcess)) { + app = command_line->GetSwitchValuePath(switches::kChildProcess) + .BaseName() + .RemoveExtension() + .MaybeAsASCII(); + } else { + base::FilePath exe_path = + command_line->GetProgram().BaseName().RemoveExtension(); + for (const auto& app_name : apps_to_debug) { + if (base::FilePath().AppendASCII(app_name) == exe_path) { + app = app_name; + break; + } + } + } + if (apps_to_debug.empty() || ContainsValue(apps_to_debug, app)) { +#if defined(OS_WIN) + base::string16 appw = base::UTF8ToUTF16(app); + base::string16 message = base::UTF8ToUTF16( + base::StringPrintf("%s - %d", app.c_str(), GetCurrentProcessId())); + MessageBox(NULL, message.c_str(), appw.c_str(), MB_OK | MB_SETFOREGROUND); +#else + LOG(ERROR) << app << " waiting for GDB. pid: " << getpid(); + base::debug::WaitForDebugger(60, true); +#endif + } + } +} + +void CallLibraryEarlyInitialization(base::NativeLibrary app_library) { + // Do whatever warming that the mojo application wants. + +#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE + typedef void (*LibraryEarlyInitFunction)(const uint8_t*); + LibraryEarlyInitFunction init_function = + reinterpret_cast<LibraryEarlyInitFunction>( + base::GetFunctionPointerFromNativeLibrary(app_library, + "InitializeBase")); + if (init_function) { + // Get the ICU data that we prewarmed in the runner and then pass it to + // the copy of icu in the mojo binary that we're running. + const uint8_t* icu_data = base::i18n::GetRawIcuMemory(); + init_function(icu_data); + } +#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE + + // TODO(erg): All chromium binaries load base. We might want to make a + // general system for other people. +} + +} // namespace shell diff --git a/chromium/services/shell/runner/init.h b/chromium/services/shell/runner/init.h new file mode 100644 index 00000000000..dced60d61c1 --- /dev/null +++ b/chromium/services/shell/runner/init.h @@ -0,0 +1,23 @@ +// Copyright 2013 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. + +#ifndef SERVICES_SHELL_RUNNER_INIT_H_ +#define SERVICES_SHELL_RUNNER_INIT_H_ + +#include "base/native_library.h" + +namespace shell { + +// Initialization routines shared by desktop and Android main functions. +void InitializeLogging(); + +void WaitForDebuggerIfNecessary(); + +// Calls "LibraryEarlyInitialization" in |app_library| if it exists. We do +// common initialization there now. +void CallLibraryEarlyInitialization(base::NativeLibrary app_library); + +} // namespace shell + +#endif // SERVICES_SHELL_RUNNER_INIT_H_ diff --git a/chromium/services/shell/shell.cc b/chromium/services/shell/shell.cc new file mode 100644 index 00000000000..c5aa2bdb798 --- /dev/null +++ b/chromium/services/shell/shell.cc @@ -0,0 +1,792 @@ +// Copyright 2014 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 "services/shell/shell.h" + +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/guid.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/process/process.h" +#include "base/process/process_handle.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/trace_event/trace_event.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/shell/connect_util.h" +#include "services/shell/public/cpp/connector.h" +#include "services/shell/public/cpp/names.h" +#include "services/shell/public/cpp/shell_connection.h" +#include "services/shell/public/interfaces/connector.mojom.h" +#include "services/shell/public/interfaces/shell.mojom.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" + +namespace shell { + +namespace { + +const char kCatalogName[] = "mojo:catalog"; +const char kShellName[] = "mojo:shell"; +const char kCapabilityClass_UserID[] = "shell:user_id"; +const char kCapabilityClass_ClientProcess[] = "shell:client_process"; +const char kCapabilityClass_InstanceName[] = "shell:instance_name"; +const char kCapabilityClass_AllUsers[] = "shell:all_users"; +const char kCapabilityClass_ExplicitClass[] = "shell:explicit_class"; + +} // namespace + +Identity CreateShellIdentity() { + return Identity(kShellName, mojom::kRootUserID); +} + +Identity CreateCatalogIdentity() { + return Identity(kCatalogName, mojom::kRootUserID); +} + +CapabilitySpec GetPermissiveCapabilities() { + CapabilitySpec capabilities; + CapabilityRequest spec; + spec.interfaces.insert("*"); + capabilities.required["*"] = spec; + return capabilities; +} + +CapabilityRequest GetCapabilityRequest(const CapabilitySpec& source_spec, + const Identity& target) { + CapabilityRequest request; + + // Start by looking for specs specific to the supplied identity. + auto it = source_spec.required.find(target.name()); + if (it != source_spec.required.end()) { + std::copy(it->second.classes.begin(), it->second.classes.end(), + std::inserter(request.classes, request.classes.begin())); + std::copy(it->second.interfaces.begin(), it->second.interfaces.end(), + std::inserter(request.interfaces, request.interfaces.begin())); + } + + // Apply wild card rules too. + it = source_spec.required.find("*"); + if (it != source_spec.required.end()) { + std::copy(it->second.classes.begin(), it->second.classes.end(), + std::inserter(request.classes, request.classes.begin())); + std::copy(it->second.interfaces.begin(), it->second.interfaces.end(), + std::inserter(request.interfaces, request.interfaces.begin())); + } + return request; +} + +CapabilityRequest GenerateCapabilityRequestForConnection( + const CapabilitySpec& source_spec, + const Identity& target, + const CapabilitySpec& target_spec) { + CapabilityRequest request = GetCapabilityRequest(source_spec, target); + // Flatten all interfaces from classes requested by the source into the + // allowed interface set in the request. + for (const auto& class_name : request.classes) { + auto it = target_spec.provided.find(class_name); + if (it != target_spec.provided.end()) { + for (const auto& interface_name : it->second) + request.interfaces.insert(interface_name); + } + } + return request; +} + +bool HasClass(const CapabilitySpec& spec, const std::string& class_name) { + auto it = spec.required.find(kShellName); + if (it == spec.required.end()) + return false; + return it->second.classes.find(class_name) != it->second.classes.end(); +} + +// Encapsulates a connection to an instance of an application, tracked by the +// shell's Shell. +class Shell::Instance : public mojom::Connector, + public mojom::PIDReceiver, + public ShellClient, + public InterfaceFactory<mojom::Shell>, + public mojom::Shell { + public: + Instance(shell::Shell* shell, + const Identity& identity, + const CapabilitySpec& capability_spec) + : shell_(shell), + id_(GenerateUniqueID()), + identity_(identity), + capability_spec_(capability_spec), + allow_any_application_(capability_spec.required.count("*") == 1), + pid_receiver_binding_(this), + weak_factory_(this) { + if (identity_.name() == kShellName || identity_.name() == kCatalogName) + pid_ = base::Process::Current().Pid(); + DCHECK_NE(mojom::kInvalidInstanceID, id_); + } + + ~Instance() override { + if (parent_) + parent_->RemoveChild(this); + // |children_| will be modified during destruction. + std::set<Instance*> children = children_; + for (auto child : children) + shell_->OnInstanceError(child); + + // Shutdown all bindings before we close the runner. This way the process + // should see the pipes closed and exit, as well as waking up any potential + // sync/WaitForIncomingResponse(). + shell_client_.reset(); + if (pid_receiver_binding_.is_bound()) + pid_receiver_binding_.Close(); + connectors_.CloseAllBindings(); + shell_bindings_.CloseAllBindings(); + // Release |runner_| so that if we are called back to OnRunnerCompleted() + // we know we're in the destructor. + std::unique_ptr<NativeRunner> runner = std::move(runner_); + runner.reset(); + } + + Instance* parent() { return parent_; } + + void AddChild(Instance* child) { + children_.insert(child); + child->parent_ = this; + } + + void RemoveChild(Instance* child) { + auto it = children_.find(child); + DCHECK(it != children_.end()); + children_.erase(it); + child->parent_ = nullptr; + } + + void ConnectToClient(std::unique_ptr<ConnectParams> params) { + CHECK(shell_client_.is_bound()); + params->connect_callback().Run(mojom::ConnectResult::SUCCEEDED, + identity_.user_id(), id_); + uint32_t source_id = mojom::kInvalidInstanceID; + CapabilityRequest request; + request.interfaces.insert("*"); + Instance* source = shell_->GetExistingInstance(params->source()); + if (source) { + request = GenerateCapabilityRequestForConnection( + source->capability_spec_, identity_, capability_spec_); + source_id = source->id(); + } + + // The target has specified that sources must request one of its provided + // classes instead of specifying a wild-card for interfaces. + if (HasClass(capability_spec_, kCapabilityClass_ExplicitClass) && + (request.interfaces.count("*") != 0)) { + request.interfaces.erase("*"); + } + + shell_client_->AcceptConnection( + mojom::Identity::From(params->source()), source_id, + params->TakeRemoteInterfaces(), params->TakeLocalInterfaces(), + mojom::CapabilityRequest::From(request), params->target().name()); + } + + void StartWithClient(mojom::ShellClientPtr client) { + CHECK(!shell_client_); + shell_client_ = std::move(client); + shell_client_.set_connection_error_handler( + base::Bind(&Instance::OnShellClientLost, base::Unretained(this), + shell_->GetWeakPtr())); + shell_client_->Initialize(mojom::Identity::From(identity_), id_, + base::Bind(&Instance::OnInitializeResponse, + base::Unretained(this))); + } + + void StartWithClientProcessConnection( + mojom::ClientProcessConnectionPtr client_process_connection) { + mojom::ShellClientPtr client; + client.Bind(mojom::ShellClientPtrInfo( + std::move(client_process_connection->shell_client), 0)); + pid_receiver_binding_.Bind( + std::move(client_process_connection->pid_receiver_request)); + StartWithClient(std::move(client)); + } + + void StartWithFilePath(const base::FilePath& path) { + CHECK(!shell_client_); + runner_ = shell_->native_runner_factory_->Create(path); + bool start_sandboxed = false; + mojom::ShellClientPtr client = runner_->Start( + path, identity_, start_sandboxed, + base::Bind(&Instance::PIDAvailable, weak_factory_.GetWeakPtr()), + base::Bind(&Instance::OnRunnerCompleted, weak_factory_.GetWeakPtr())); + StartWithClient(std::move(client)); + } + + mojom::InstanceInfoPtr CreateInstanceInfo() const { + mojom::InstanceInfoPtr info(mojom::InstanceInfo::New()); + info->id = id_; + info->identity = mojom::Identity::From(identity_); + info->pid = pid_; + return info; + } + + const CapabilitySpec& capability_spec() const { + return capability_spec_; + } + const Identity& identity() const { return identity_; } + uint32_t id() const { return id_; } + + // ShellClient: + bool AcceptConnection(Connection* connection) override { + connection->AddInterface<mojom::Shell>(this); + return true; + } + + private: + // mojom::Connector implementation: + void Connect(mojom::IdentityPtr target_ptr, + mojom::InterfaceProviderRequest remote_interfaces, + mojom::InterfaceProviderPtr local_interfaces, + mojom::ClientProcessConnectionPtr client_process_connection, + const ConnectCallback& callback) override { + Identity target = target_ptr.To<Identity>(); + if (target.user_id() == mojom::kInheritUserID) + target.set_user_id(identity_.user_id()); + + if (!ValidateIdentity(target, callback)) + return; + if (!ValidateClientProcessConnection(&client_process_connection, target, + callback)) { + return; + } + if (!ValidateCapabilities(target, callback)) + return; + + std::unique_ptr<ConnectParams> params(new ConnectParams); + params->set_source(identity_); + params->set_target(target); + params->set_remote_interfaces(std::move(remote_interfaces)); + params->set_local_interfaces(std::move(local_interfaces)); + params->set_client_process_connection(std::move(client_process_connection)); + params->set_connect_callback(callback); + shell_->Connect(std::move(params)); + } + + void Clone(mojom::ConnectorRequest request) override { + connectors_.AddBinding(this, std::move(request)); + } + + // mojom::PIDReceiver: + void SetPID(uint32_t pid) override { + PIDAvailable(pid); + } + + // InterfaceFactory<mojom::Shell>: + void Create(Connection* connection, + mojom::ShellRequest request) override { + shell_bindings_.AddBinding(this, std::move(request)); + } + + // mojom::Shell implementation: + void AddInstanceListener(mojom::InstanceListenerPtr listener) override { + // TODO(beng): this should only track the instances matching this user, and + // root. + shell_->AddInstanceListener(std::move(listener)); + } + + bool ValidateIdentity(const Identity& identity, + const ConnectCallback& callback) { + if (!IsValidName(identity.name())) { + LOG(ERROR) << "Error: invalid Name: " << identity.name(); + callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, + mojom::kInheritUserID, mojom::kInvalidInstanceID); + return false; + } + if (!base::IsValidGUID(identity.user_id())) { + LOG(ERROR) << "Error: invalid user_id: " << identity.user_id(); + callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, + mojom::kInheritUserID, mojom::kInvalidInstanceID); + return false; + } + return true; + } + + bool ValidateClientProcessConnection( + mojom::ClientProcessConnectionPtr* client_process_connection, + const Identity& target, + const ConnectCallback& callback) { + if (!client_process_connection->is_null()) { + if (!HasClass(capability_spec_, kCapabilityClass_ClientProcess)) { + LOG(ERROR) << "Instance: " << identity_.name() << " attempting " + << "to register an instance for a process it created for " + << "target: " << target.name() << " without the " + << "mojo:shell{client_process} capability class."; + callback.Run(mojom::ConnectResult::ACCESS_DENIED, + mojom::kInheritUserID, mojom::kInvalidInstanceID); + return false; + } + + if (!(*client_process_connection)->shell_client.is_valid() || + !(*client_process_connection)->pid_receiver_request.is_valid()) { + LOG(ERROR) << "Must supply both shell_client AND " + << "pid_receiver_request when sending " + << "client_process_connection."; + callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, + mojom::kInheritUserID, mojom::kInvalidInstanceID); + return false; + } + if (shell_->GetExistingInstance(target)) { + LOG(ERROR) << "Cannot client process matching existing identity:" + << "Name: " << target.name() << " User: " + << target.user_id() << " Instance: " << target.instance(); + callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, + mojom::kInheritUserID, mojom::kInvalidInstanceID); + return false; + } + } + return true; + } + + bool ValidateCapabilities(const Identity& target, + const ConnectCallback& callback) { + // TODO(beng): Need to do the following additional policy validation of + // whether this instance is allowed to connect using: + // - a non-null client_process_connection. + if (target.user_id() != identity_.user_id() && + target.user_id() != mojom::kRootUserID && + !HasClass(capability_spec_, kCapabilityClass_UserID)) { + LOG(ERROR) << "Instance: " << identity_.name() << " running as: " + << identity_.user_id() << " attempting to connect to: " + << target.name() << " as: " << target.user_id() << " without " + << " the mojo:shell{user_id} capability class."; + callback.Run(mojom::ConnectResult::ACCESS_DENIED, + mojom::kInheritUserID, mojom::kInvalidInstanceID); + return false; + } + if (!target.instance().empty() && + target.instance() != GetNamePath(target.name()) && + !HasClass(capability_spec_, kCapabilityClass_InstanceName)) { + LOG(ERROR) << "Instance: " << identity_.name() << " attempting to " + << "connect to " << target.name() << " using Instance name: " + << target.instance() << " without the " + << "mojo:shell{instance_name} capability class."; + callback.Run(mojom::ConnectResult::ACCESS_DENIED, + mojom::kInheritUserID, mojom::kInvalidInstanceID); + return false; + + } + + if (allow_any_application_ || + capability_spec_.required.find(target.name()) != + capability_spec_.required.end()) { + return true; + } + LOG(ERROR) << "Capabilities prevented connection from: " << + identity_.name() << " to: " << target.name(); + callback.Run(mojom::ConnectResult::ACCESS_DENIED, + mojom::kInheritUserID, mojom::kInvalidInstanceID); + return false; + } + + uint32_t GenerateUniqueID() const { + static uint32_t id = mojom::kInvalidInstanceID; + ++id; + CHECK_NE(mojom::kInvalidInstanceID, id); + return id; + } + + void PIDAvailable(base::ProcessId pid) { + if (pid == base::kNullProcessId) { + shell_->OnInstanceError(this); + return; + } + pid_ = pid; + shell_->NotifyPIDAvailable(id_, pid_); + } + + void OnShellClientLost(base::WeakPtr<shell::Shell> shell) { + shell_client_.reset(); + OnConnectionLost(shell); + } + + void OnConnectionLost(base::WeakPtr<shell::Shell> shell) { + // Any time a Connector is lost or we lose the ShellClient connection, it + // may have been the last pipe using this Instance. If so, clean up. + if (shell && connectors_.empty() && !shell_client_) { + // Deletes |this|. + shell->OnInstanceError(this); + } + } + + void OnInitializeResponse(mojom::ConnectorRequest connector_request) { + if (connector_request.is_pending()) { + connectors_.AddBinding(this, std::move(connector_request)); + connectors_.set_connection_error_handler( + base::Bind(&Instance::OnConnectionLost, base::Unretained(this), + shell_->GetWeakPtr())); + } + } + + // Callback when NativeRunner completes. + void OnRunnerCompleted() { + if (!runner_.get()) + return; // We're in the destructor. + + shell_->OnInstanceError(this); + } + + shell::Shell* const shell_; + + // An id that identifies this instance. Distinct from pid, as a single process + // may vend multiple application instances, and this object may exist before a + // process is launched. + const uint32_t id_; + const Identity identity_; + const CapabilitySpec capability_spec_; + const bool allow_any_application_; + std::unique_ptr<NativeRunner> runner_; + mojom::ShellClientPtr shell_client_; + mojo::Binding<mojom::PIDReceiver> pid_receiver_binding_; + mojo::BindingSet<mojom::Connector> connectors_; + mojo::BindingSet<mojom::Shell> shell_bindings_; + base::ProcessId pid_ = base::kNullProcessId; + Instance* parent_ = nullptr; + std::set<Instance*> children_; + base::WeakPtrFactory<Instance> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Instance); +}; + +// static +Shell::TestAPI::TestAPI(Shell* shell) : shell_(shell) {} +Shell::TestAPI::~TestAPI() {} + +bool Shell::TestAPI::HasRunningInstanceForName(const std::string& name) const { + for (const auto& entry : shell_->identity_to_instance_) { + if (entry.first.name() == name) + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Shell, public: + +Shell::Shell(std::unique_ptr<NativeRunnerFactory> native_runner_factory, + mojom::ShellClientPtr catalog) + : native_runner_factory_(std::move(native_runner_factory)), + weak_ptr_factory_(this) { + mojom::ShellClientPtr client; + mojom::ShellClientRequest request = mojo::GetProxy(&client); + Instance* instance = CreateInstance(Identity(), CreateShellIdentity(), + GetPermissiveCapabilities()); + instance->StartWithClient(std::move(client)); + singletons_.insert(kShellName); + shell_connection_.reset(new ShellConnection(this, std::move(request))); + + if (catalog) + InitCatalog(std::move(catalog)); +} + +Shell::~Shell() { + TerminateShellConnections(); + // Terminate any remaining instances. + while (!identity_to_instance_.empty()) + OnInstanceError(identity_to_instance_.begin()->second); + identity_to_resolver_.clear(); +} + +void Shell::SetInstanceQuitCallback( + base::Callback<void(const Identity&)> callback) { + instance_quit_callback_ = callback; +} + +void Shell::Connect(std::unique_ptr<ConnectParams> params) { + Connect(std::move(params), nullptr); +} + +mojom::ShellClientRequest Shell::InitInstanceForEmbedder( + const std::string& name) { + std::unique_ptr<ConnectParams> params(new ConnectParams); + + Identity embedder_identity(name, mojom::kRootUserID); + params->set_source(embedder_identity); + params->set_target(embedder_identity); + + mojom::ShellClientPtr client; + mojom::ShellClientRequest request = mojo::GetProxy(&client); + Connect(std::move(params), std::move(client)); + + return request; +} + +//////////////////////////////////////////////////////////////////////////////// +// Shell, ShellClient implementation: + +bool Shell::AcceptConnection(Connection* connection) { + // The only interface we expose is mojom::Shell, and access to this interface + // is brokered by a policy specific to each caller, managed by the caller's + // instance. Here we look to see who's calling, and forward to the caller's + // instance to continue. + Instance* instance = nullptr; + for (const auto& entry : identity_to_instance_) { + if (entry.second->id() == connection->GetRemoteInstanceID()) { + instance = entry.second; + break; + } + } + DCHECK(instance); + return instance->AcceptConnection(connection); +} + +//////////////////////////////////////////////////////////////////////////////// +// Shell, private: + +void Shell::InitCatalog(mojom::ShellClientPtr catalog) { + // TODO(beng): It'd be great to build this from the manifest, however there's + // a bit of a chicken-and-egg problem. + CapabilitySpec spec; + Interfaces interfaces; + interfaces.insert("filesystem::mojom::Directory"); + spec.provided["app"] = interfaces; + Instance* instance = CreateInstance(CreateShellIdentity(), + CreateCatalogIdentity(), + spec); + singletons_.insert(kCatalogName); + instance->StartWithClient(std::move(catalog)); +} + +mojom::ShellResolver* Shell::GetResolver(const Identity& identity) { + auto iter = identity_to_resolver_.find(identity); + if (iter != identity_to_resolver_.end()) + return iter->second.get(); + + mojom::ShellResolverPtr resolver_ptr; + ConnectToInterface(this, identity, CreateCatalogIdentity(), &resolver_ptr); + mojom::ShellResolver* resolver = resolver_ptr.get(); + identity_to_resolver_[identity] = std::move(resolver_ptr); + return resolver; +} + +void Shell::TerminateShellConnections() { + Instance* instance = GetExistingInstance(CreateShellIdentity()); + DCHECK(instance); + OnInstanceError(instance); +} + +void Shell::OnInstanceError(Instance* instance) { + const Identity identity = instance->identity(); + // Remove the shell. + auto it = identity_to_instance_.find(identity); + DCHECK(it != identity_to_instance_.end()); + int id = instance->id(); + identity_to_instance_.erase(it); + instance_listeners_.ForAllPtrs([this, id](mojom::InstanceListener* listener) { + listener->InstanceDestroyed(id); + }); + delete instance; + if (!instance_quit_callback_.is_null()) + instance_quit_callback_.Run(identity); +} + +void Shell::Connect(std::unique_ptr<ConnectParams> params, + mojom::ShellClientPtr client) { + TRACE_EVENT_INSTANT1("mojo_shell", "Shell::Connect", + TRACE_EVENT_SCOPE_THREAD, "original_name", + params->target().name()); + DCHECK(IsValidName(params->target().name())); + DCHECK(base::IsValidGUID(params->target().user_id())); + DCHECK_NE(mojom::kInheritUserID, params->target().user_id()); + DCHECK(!client.is_bound() || !identity_to_instance_.count(params->target())); + + // Connect to an existing matching instance, if possible. + if (!client.is_bound() && ConnectToExistingInstance(¶ms)) + return; + + // The catalog needs to see the source identity as that of the originating + // app so it loads the correct store. Since the catalog is itself run as root + // when this re-enters Connect() it'll be handled by + // ConnectToExistingInstance(). + mojom::ShellResolver* resolver = + GetResolver(Identity(kShellName, params->target().user_id())); + + std::string name = params->target().name(); + resolver->ResolveMojoName( + name, base::Bind(&shell::Shell::OnGotResolvedName, + weak_ptr_factory_.GetWeakPtr(), base::Passed(¶ms), + base::Passed(&client))); +} + +Shell::Instance* Shell::GetExistingInstance(const Identity& identity) const { + const auto& it = identity_to_instance_.find(identity); + Instance* instance = it != identity_to_instance_.end() ? it->second : nullptr; + if (instance) + return instance; + + if (singletons_.find(identity.name()) != singletons_.end()) { + for (auto entry : identity_to_instance_) { + if (entry.first.name() == identity.name() && + entry.first.instance() == identity.instance()) { + return entry.second; + } + } + } + return nullptr; +} + +void Shell::NotifyPIDAvailable(uint32_t id, base::ProcessId pid) { + instance_listeners_.ForAllPtrs([id, pid](mojom::InstanceListener* listener) { + listener->InstancePIDAvailable(id, pid); + }); +} + +bool Shell::ConnectToExistingInstance(std::unique_ptr<ConnectParams>* params) { + Instance* instance = GetExistingInstance((*params)->target()); + if (instance) + instance->ConnectToClient(std::move(*params)); + return !!instance; +} + +Shell::Instance* Shell::CreateInstance(const Identity& source, + const Identity& target, + const CapabilitySpec& spec) { + CHECK(target.user_id() != mojom::kInheritUserID); + Instance* instance = new Instance(this, target, spec); + DCHECK(identity_to_instance_.find(target) == + identity_to_instance_.end()); + Instance* source_instance = GetExistingInstance(source); + if (source_instance) + source_instance->AddChild(instance); + identity_to_instance_[target] = instance; + mojom::InstanceInfoPtr info = instance->CreateInstanceInfo(); + instance_listeners_.ForAllPtrs([&info](mojom::InstanceListener* listener) { + listener->InstanceCreated(info.Clone()); + }); + return instance; +} + +void Shell::AddInstanceListener(mojom::InstanceListenerPtr listener) { + // TODO(beng): filter instances provided by those visible to this client. + mojo::Array<mojom::InstanceInfoPtr> instances; + for (auto& instance : identity_to_instance_) + instances.push_back(instance.second->CreateInstanceInfo()); + listener->SetExistingInstances(std::move(instances)); + + instance_listeners_.AddPtr(std::move(listener)); +} + +void Shell::CreateShellClientWithFactory(const Identity& shell_client_factory, + const std::string& name, + mojom::ShellClientRequest request) { + mojom::ShellClientFactory* factory = + GetShellClientFactory(shell_client_factory); + factory->CreateShellClient(std::move(request), name); +} + +mojom::ShellClientFactory* Shell::GetShellClientFactory( + const Identity& shell_client_factory_identity) { + auto it = shell_client_factories_.find(shell_client_factory_identity); + if (it != shell_client_factories_.end()) + return it->second.get(); + + Identity source_identity(kShellName, mojom::kInheritUserID); + mojom::ShellClientFactoryPtr factory; + ConnectToInterface(this, source_identity, shell_client_factory_identity, + &factory); + mojom::ShellClientFactory* factory_interface = factory.get(); + factory.set_connection_error_handler(base::Bind( + &shell::Shell::OnShellClientFactoryLost, weak_ptr_factory_.GetWeakPtr(), + shell_client_factory_identity)); + shell_client_factories_[shell_client_factory_identity] = std::move(factory); + return factory_interface; +} + +void Shell::OnShellClientFactoryLost(const Identity& which) { + // Remove the mapping. + auto it = shell_client_factories_.find(which); + DCHECK(it != shell_client_factories_.end()); + shell_client_factories_.erase(it); +} + +void Shell::OnGotResolvedName(std::unique_ptr<ConnectParams> params, + mojom::ShellClientPtr client, + mojom::ResolveResultPtr result) { + std::string instance_name = params->target().instance(); + if (instance_name == GetNamePath(params->target().name()) && + result->qualifier != GetNamePath(result->resolved_name)) { + instance_name = result->qualifier; + } + Identity target(params->target().name(), params->target().user_id(), + instance_name); + params->set_target(target); + + // It's possible that when this manifest request was issued, another one was + // already in-progress and completed by the time this one did, and so the + // requested application may already be running. + if (ConnectToExistingInstance(¶ms)) + return; + + Identity source = params->source(); + // |capabilities_ptr| can be null when there is no manifest, e.g. for URL + // types not resolvable by the resolver. + CapabilitySpec capabilities = GetPermissiveCapabilities(); + if (!result->capabilities.is_null()) + capabilities = result->capabilities.To<CapabilitySpec>(); + + // Clients that request "all_users" class from the shell are allowed to + // field connection requests from any user. They also run with a synthetic + // user id generated here. The user id provided via Connect() is ignored. + // Additionally apps with the "all_users" class are not tied to the lifetime + // of the app that connected to them, instead they are owned by the shell. + Identity source_identity_for_creation; + if (HasClass(capabilities, kCapabilityClass_AllUsers)) { + singletons_.insert(target.name()); + target.set_user_id(base::GenerateGUID()); + source_identity_for_creation = CreateShellIdentity(); + } else { + source_identity_for_creation = params->source(); + } + + mojom::ClientProcessConnectionPtr client_process_connection = + params->TakeClientProcessConnection(); + Instance* instance = CreateInstance(source_identity_for_creation, + target, capabilities); + + // Below are various paths through which a new Instance can be bound to a + // ShellClient proxy. + if (client.is_bound()) { + // If a ShellClientPtr was provided, there's no more work to do: someone + // is already holding a corresponding ShellClientRequest. + instance->StartWithClient(std::move(client)); + } else if (!client_process_connection.is_null()) { + // Likewise if a ClientProcessConnection was given via Connect(), it + // provides the ShellClient proxy to use. + instance->StartWithClientProcessConnection( + std::move(client_process_connection)); + } else { + // Otherwise we create a new ShellClient pipe. + mojom::ShellClientRequest request = GetProxy(&client); + CHECK(!result->package_path.empty() && !result->capabilities.is_null()); + + if (target.name() != result->resolved_name) { + instance->StartWithClient(std::move(client)); + Identity factory(result->resolved_name, target.user_id(), + instance_name); + CreateShellClientWithFactory(factory, target.name(), + std::move(request)); + } else { + instance->StartWithFilePath(result->package_path); + } + } + + // Now that the instance has a ShellClient, we can connect to it. + instance->ConnectToClient(std::move(params)); +} + +base::WeakPtr<Shell> Shell::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +} // namespace shell diff --git a/chromium/services/shell/shell.gyp b/chromium/services/shell/shell.gyp new file mode 100644 index 00000000000..054a10d459e --- /dev/null +++ b/chromium/services/shell/shell.gyp @@ -0,0 +1,205 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'shell_lib', + 'type': 'static_library', + 'sources': [ + '../catalog/catalog.cc', + '../catalog/catalog.h', + '../catalog/constants.cc', + '../catalog/constants.h', + '../catalog/entry.cc', + '../catalog/entry.h', + '../catalog/instance.cc', + '../catalog/instance.h', + '../catalog/reader.cc', + '../catalog/reader.h', + '../catalog/store.cc', + '../catalog/store.h', + '../catalog/types.h', + 'connect_params.cc', + 'connect_params.h', + 'connect_util.cc', + 'connect_util.h', + 'native_runner.h', + 'native_runner_delegate.h', + 'shell.cc', + 'shell.h', + 'switches.cc', + 'switches.cc', + ], + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '<(DEPTH)/components/filesystem/filesystem.gyp:filesystem_bindings', + '<(DEPTH)/components/filesystem/filesystem.gyp:filesystem_lib', + '<(DEPTH)/mojo/mojo_base.gyp:mojo_common_lib', + 'shell_public.gyp:shell_public', + ], + 'export_dependent_settings': [ + '<(DEPTH)/components/filesystem/filesystem.gyp:filesystem_bindings', + 'shell_public.gyp:shell_public', + ], + 'variables': { + 'mojom_typemaps': [ + '<(DEPTH)/mojo/common/common_custom_types.typemap', + ], + } + }, { + 'target_name': 'mojo_shell_unittests', + 'type': 'executable', + 'sources': [ + 'tests/placeholder_unittest.cc', + ], + 'dependencies': [ + 'shell_lib', + 'shell_test_public', + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/mojo/mojo_base.gyp:mojo_common_lib', + '<(DEPTH)/mojo/mojo_edk.gyp:mojo_run_all_unittests', + '<(DEPTH)/mojo/mojo_public.gyp:mojo_cpp_bindings', + '<(DEPTH)/testing/gtest.gyp:gtest', + 'shell_public.gyp:shell_public', + ] + }, { + 'target_name': 'shell_test_public', + 'type': 'static_library', + 'dependencies': [ + 'shell_test_interfaces', + ], + }, { + 'target_name': 'shell_test_interfaces', + 'type': 'none', + 'variables': { + 'mojom_files': [ + 'tests/test.mojom', + ], + }, + 'includes': [ + '../../mojo/mojom_bindings_generator_explicit.gypi', + ], + }, { + 'target_name': 'shell_runner_common_lib', + 'type': 'static_library', + 'sources': [ + 'runner/common/client_util.cc', + 'runner/common/client_util.h', + 'runner/common/switches.cc', + 'runner/common/switches.h', + ], + 'include_dirs': [ + '..', + ], + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/mojo/mojo_edk.gyp:mojo_system_impl', + '<(DEPTH)/mojo/mojo_public.gyp:mojo_cpp_bindings', + '<(DEPTH)/mojo/mojo_public.gyp:mojo_cpp_system', + 'shell_public.gyp:shell_public', + ], + 'export_dependent_settings': [ + 'shell_public.gyp:shell_public', + ], + }, { + 'target_name': 'shell_runner_host_lib', + 'type': 'static_library', + 'sources': [ + 'runner/host/child_process.cc', + 'runner/host/child_process.h', + 'runner/host/child_process_base.cc', + 'runner/host/child_process_base.h', + 'runner/host/child_process_host.cc', + 'runner/host/child_process_host.h', + 'runner/host/in_process_native_runner.cc', + 'runner/host/in_process_native_runner.h', + 'runner/host/native_application_support.cc', + 'runner/host/native_application_support.h', + 'runner/host/out_of_process_native_runner.cc', + 'runner/host/out_of_process_native_runner.h', + 'runner/init.cc', + 'runner/init.h', + ], + 'dependencies': [ + 'shell_lib', + 'shell_runner_common_lib', + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/base/base.gyp:base_i18n', + '<(DEPTH)/base/base.gyp:base_static', + '<(DEPTH)/mojo/mojo_edk.gyp:mojo_system_impl', + '<(DEPTH)/mojo/mojo_platform_handle.gyp:platform_handle', + 'shell_public.gyp:shell_public', + ], + 'export_dependent_settings': [ + 'shell_public.gyp:shell_public', + ], + 'conditions': [ + ['OS=="linux"', { + 'sources': [ + 'runner/host/linux_sandbox.cc', + 'runner/host/linux_sandbox.h', + ], + 'dependencies': [ + '<(DEPTH)/sandbox/sandbox.gyp:sandbox', + '<(DEPTH)/sandbox/sandbox.gyp:sandbox_services', + '<(DEPTH)/sandbox/sandbox.gyp:seccomp_bpf', + '<(DEPTH)/sandbox/sandbox.gyp:seccomp_bpf_helpers', + ], + }], + ['OS=="mac"', { + 'sources': [ + 'runner/host/mach_broker.cc', + 'runner/host/mach_broker.h', + ], + }], + ], + }, { + # GN version: //services/catalog:manifest + 'target_name': 'catalog_manifest', + 'type': 'none', + 'variables': { + 'application_type': 'mojo', + 'application_name': 'catalog', + 'source_manifest': '<(DEPTH)/services/catalog/manifest.json', + }, + 'includes': [ + '../../mojo/public/mojo_application_manifest.gypi', + ], + 'hard_dependency': 1, + }, { + # GN version: //services/shell/public/cpp/tests + 'target_name': 'shell_client_lib_unittests', + 'type': 'executable', + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/mojo/mojo_edk.gyp:mojo_run_all_unittests', + '<(DEPTH)/testing/gtest.gyp:gtest', + 'shell_public.gyp:shell_public', + ], + 'sources': [ + 'public/cpp/tests/interface_registry_unittest.cc', + ], + }], + 'conditions': [ + ['test_isolation_mode != "noop"', { + 'targets': [ + { + 'target_name': 'mojo_shell_unittests_run', + 'type': 'none', + 'dependencies': [ + 'mojo_shell_unittests', + ], + 'includes': [ + '../../build/isolate.gypi', + ], + 'sources': [ + 'mojo_shell_unittests.isolate', + ], + }, + ], + }], + ], +} diff --git a/chromium/services/shell/shell.h b/chromium/services/shell/shell.h new file mode 100644 index 00000000000..4f7afde70af --- /dev/null +++ b/chromium/services/shell/shell.h @@ -0,0 +1,172 @@ +// Copyright 2014 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. + +#ifndef SERVICES_SHELL_SHELL_H_ +#define SERVICES_SHELL_SHELL_H_ + +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" +#include "services/shell/connect_params.h" +#include "services/shell/native_runner.h" +#include "services/shell/public/cpp/capabilities.h" +#include "services/shell/public/cpp/identity.h" +#include "services/shell/public/cpp/interface_factory.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/shell/public/interfaces/connector.mojom.h" +#include "services/shell/public/interfaces/interface_provider.mojom.h" +#include "services/shell/public/interfaces/shell.mojom.h" +#include "services/shell/public/interfaces/shell_client.mojom.h" +#include "services/shell/public/interfaces/shell_client_factory.mojom.h" +#include "services/shell/public/interfaces/shell_resolver.mojom.h" + +namespace shell { +class ShellConnection; + +// Creates an identity for the Shell, used when the Shell connects to +// applications. +Identity CreateShellIdentity(); + +class Shell : public ShellClient { + public: + // API for testing. + class TestAPI { + public: + explicit TestAPI(Shell* shell); + ~TestAPI(); + + // Returns true if there is a Instance for this name. + bool HasRunningInstanceForName(const std::string& name) const; + private: + Shell* shell_; + + DISALLOW_COPY_AND_ASSIGN(TestAPI); + }; + + // |native_runner_factory| is an instance of an object capable of vending + // implementations of NativeRunner, e.g. for in or out-of-process execution. + // See native_runner.h and RunNativeApplication(). + // |file_task_runner| provides access to a thread to perform file copy + // operations on. + Shell(std::unique_ptr<NativeRunnerFactory> native_runner_factory, + mojom::ShellClientPtr catalog); + ~Shell() override; + + // Provide a callback to be notified whenever an instance is destroyed. + // Typically the creator of the Shell will use this to determine when some set + // of instances it created are destroyed, so it can shut down. + void SetInstanceQuitCallback(base::Callback<void(const Identity&)> callback); + + // Completes a connection between a source and target application as defined + // by |params|, exchanging InterfaceProviders between them. If no existing + // instance of the target application is running, one will be loaded. + void Connect(std::unique_ptr<ConnectParams> params); + + // Creates a new Instance identified as |name|. This is intended for use by + // the Shell's embedder to register itself with the shell. This must only be + // called once. + mojom::ShellClientRequest InitInstanceForEmbedder(const std::string& name); + + private: + class Instance; + + // ShellClient: + bool AcceptConnection(Connection* connection) override; + + void InitCatalog(mojom::ShellClientPtr catalog); + + // Returns the resolver to use for the specified identity. + // NOTE: ShellResolvers are cached to ensure we service requests in order. If + // we use a separate ShellResolver for each request ordering is not + // guaranteed and can lead to random flake. + mojom::ShellResolver* GetResolver(const Identity& identity); + + // Destroys all Shell-ends of connections established with Applications. + // Applications connected by this Shell will observe pipe errors and have a + // chance to shutdown. + void TerminateShellConnections(); + + // Removes a Instance when it encounters an error. + void OnInstanceError(Instance* instance); + + // Completes a connection between a source and target application as defined + // by |params|, exchanging InterfaceProviders between them. If no existing + // instance of the target application is running, one will be loaded. + // + // If |client| is not null, there must not be an instance of the target + // application already running. The shell will create a new instance and use + // |client| to control it. + void Connect(std::unique_ptr<ConnectParams> params, + mojom::ShellClientPtr client); + + // Returns a running instance matching |identity|. This might be an instance + // running as a different user if one is available that services all users. + Instance* GetExistingInstance(const Identity& identity) const; + + void NotifyPIDAvailable(uint32_t id, base::ProcessId pid); + + // Attempt to complete the connection requested by |params| by connecting to + // an existing instance. If there is an existing instance, |params| is taken, + // and this function returns true. + bool ConnectToExistingInstance(std::unique_ptr<ConnectParams>* params); + + Instance* CreateInstance(const Identity& source, + const Identity& target, + const CapabilitySpec& spec); + + // Called from the instance implementing mojom::Shell. + void AddInstanceListener(mojom::InstanceListenerPtr listener); + + void CreateShellClientWithFactory(const Identity& shell_client_factory, + const std::string& name, + mojom::ShellClientRequest request); + // Returns a running ShellClientFactory for |shell_client_factory_identity|. + // If there is not one running one is started for |source_identity|. + mojom::ShellClientFactory* GetShellClientFactory( + const Identity& shell_client_factory_identity); + void OnShellClientFactoryLost(const Identity& which); + + // Callback when remote Catalog resolves mojo:foo to mojo:bar. + // |params| are the params passed to Connect(). + // |client| if provided is a ShellClientPtr which should be used to manage the + // new application instance. This may be null. + // |result| contains the result of the resolve operation. + void OnGotResolvedName(std::unique_ptr<ConnectParams> params, + mojom::ShellClientPtr client, + mojom::ResolveResultPtr result); + + base::WeakPtr<Shell> GetWeakPtr(); + + std::map<Identity, Instance*> identity_to_instance_; + + // Tracks the names of instances that are allowed to field connection requests + // from all users. + std::set<std::string> singletons_; + + std::map<Identity, mojom::ShellClientFactoryPtr> shell_client_factories_; + // Counter used to assign ids to client factories. + uint32_t shell_client_factory_id_counter_; + + std::map<Identity, mojom::ShellResolverPtr> identity_to_resolver_; + + mojo::InterfacePtrSet<mojom::InstanceListener> instance_listeners_; + + base::Callback<void(const Identity&)> instance_quit_callback_; + std::unique_ptr<NativeRunnerFactory> native_runner_factory_; + std::unique_ptr<ShellConnection> shell_connection_; + base::WeakPtrFactory<Shell> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(Shell); +}; + +mojom::Connector::ConnectCallback EmptyConnectCallback(); + +} // namespace shell + +#endif // SERVICES_SHELL_SHELL_H_ diff --git a/chromium/services/shell/shell_public.gyp b/chromium/services/shell/shell_public.gyp new file mode 100644 index 00000000000..c8b248725e7 --- /dev/null +++ b/chromium/services/shell/shell_public.gyp @@ -0,0 +1,73 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'shell_interfaces', + 'type': 'none', + 'variables': { + 'mojom_files': [ + '../catalog/public/interfaces/catalog.mojom', + 'public/interfaces/capabilities.mojom', + 'public/interfaces/connector.mojom', + 'public/interfaces/interface_provider.mojom', + 'public/interfaces/shell.mojom', + 'public/interfaces/shell_client.mojom', + 'public/interfaces/shell_client_factory.mojom', + 'public/interfaces/shell_resolver.mojom', + ], + 'mojom_typemaps': [ + '<(DEPTH)/mojo/common/common_custom_types.typemap', + ], + }, + 'includes': [ '../../mojo/mojom_bindings_generator_explicit.gypi' ], + 'dependencies': [ + '<(DEPTH)/mojo/mojo_base.gyp:mojo_common_custom_types_mojom', + ], + 'export_dependent_settings': [ + '<(DEPTH)/mojo/mojo_base.gyp:mojo_common_custom_types_mojom', + ], + }, + { + # GN version: //services/shell/public/cpp + 'target_name': 'shell_public', + 'type': 'static_library', + 'sources': [ + 'public/cpp/application_runner.h', + 'public/cpp/capabilities.h', + 'public/cpp/connect.h', + 'public/cpp/connection.h', + 'public/cpp/connector.h', + 'public/cpp/identity.h', + 'public/cpp/interface_binder.h', + 'public/cpp/interface_factory.h', + 'public/cpp/interface_factory_impl.h', + 'public/cpp/interface_registry.h', + 'public/cpp/lib/application_runner.cc', + 'public/cpp/lib/capabilities.cc', + 'public/cpp/lib/connection_impl.cc', + 'public/cpp/lib/connection_impl.h', + 'public/cpp/lib/connector_impl.cc', + 'public/cpp/lib/connector_impl.h', + 'public/cpp/lib/identity.cc', + 'public/cpp/lib/interface_factory_binder.h', + 'public/cpp/lib/interface_registry.cc', + 'public/cpp/lib/names.cc', + 'public/cpp/lib/shell_client.cc', + 'public/cpp/lib/shell_connection.cc', + 'public/cpp/lib/shell_connection_ref.cc', + 'public/cpp/names.h', + 'public/cpp/shell_client.h', + 'public/cpp/shell_connection.h', + 'public/cpp/shell_connection_ref.h', + ], + 'dependencies': [ + 'shell_interfaces', + '<(DEPTH)/base/base.gyp:base_i18n', + '<(DEPTH)/mojo/mojo_public.gyp:mojo_cpp_bindings', + ], + }, + ], +} diff --git a/chromium/services/shell/standalone/BUILD.gn b/chromium/services/shell/standalone/BUILD.gn new file mode 100644 index 00000000000..9eb1c021a5f --- /dev/null +++ b/chromium/services/shell/standalone/BUILD.gn @@ -0,0 +1,60 @@ +# Copyright 2014 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. + +import("//mojo/public/mojo_application.gni") +import("//mojo/public/tools/bindings/mojom.gni") +import("//testing/test.gni") + +executable("standalone") { + output_name = "mojo_runner" + sources = [ + "desktop/main.cc", + ] + deps = [ + ":lib", + "//base", + "//build/config/sanitizers:deps", + "//build/win:default_exe_manifest", + ] +} + +source_set("lib") { + sources = [ + "context.cc", + "context.h", + "desktop/launcher_process.cc", + "desktop/launcher_process.h", + "desktop/main_helper.cc", + "desktop/main_helper.h", + "tracer.cc", + "tracer.h", + ] + + deps = [ + "//base", + "//base:base_static", + "//base/third_party/dynamic_annotations", + "//components/tracing:startup_tracing", + "//mojo/edk/system", + "//services/catalog:lib", + "//services/shell", + "//services/shell/public/cpp", + "//services/shell/runner/host:lib", + "//services/tracing/public/cpp", + "//services/tracing/public/interfaces", + "//url", + ] + + data_deps = [ + "//services/tracing", + ] + + # This target includes some files behind #ifdef OS... guards. Since gn is not + # smart enough to understand preprocess includes, it does complains about + # these includes when not using the build files for that OS. Suppress checking + # so we can enable checking for the rest of the targets in this file. + # TODO: Might be better to split the files with OS-specific includes out to a + # separate source_set so we can leave checking on for the rest of the target. + check_includes = false +} diff --git a/chromium/services/shell/standalone/DEPS b/chromium/services/shell/standalone/DEPS new file mode 100644 index 00000000000..4af70245fc9 --- /dev/null +++ b/chromium/services/shell/standalone/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+components/tracing", + "+services/catalog", + "+services/tracing", +] diff --git a/chromium/services/shell/standalone/context.cc b/chromium/services/shell/standalone/context.cc new file mode 100644 index 00000000000..9eb3cb2dc05 --- /dev/null +++ b/chromium/services/shell/standalone/context.cc @@ -0,0 +1,262 @@ +// Copyright 2013 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 "services/shell/standalone/context.h" + +#include <stddef.h> +#include <stdint.h> + +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/lazy_instance.h" +#include "base/macros.h" +#include "base/path_service.h" +#include "base/process/process_info.h" +#include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/trace_event/trace_event.h" +#include "build/build_config.h" +#include "components/tracing/tracing_switches.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/util/filename_util.h" +#include "services/catalog/catalog.h" +#include "services/catalog/store.h" +#include "services/shell/connect_params.h" +#include "services/shell/public/cpp/names.h" +#include "services/shell/runner/host/in_process_native_runner.h" +#include "services/shell/runner/host/out_of_process_native_runner.h" +#include "services/shell/standalone/tracer.h" +#include "services/shell/switches.h" +#include "services/tracing/public/cpp/switches.h" +#include "services/tracing/public/cpp/trace_provider_impl.h" +#include "services/tracing/public/cpp/tracing_impl.h" +#include "services/tracing/public/interfaces/tracing.mojom.h" + +#if defined(OS_MACOSX) +#include "services/shell/runner/host/mach_broker.h" +#endif + +namespace shell { +namespace { + +// Used to ensure we only init once. +class Setup { + public: + Setup() { mojo::edk::Init(); } + + ~Setup() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Setup); +}; + +class TracingInterfaceProvider : public mojom::InterfaceProvider { + public: + TracingInterfaceProvider(Tracer* tracer, + mojom::InterfaceProviderRequest request) + : tracer_(tracer), binding_(this, std::move(request)) {} + ~TracingInterfaceProvider() override {} + + // mojom::InterfaceProvider: + void GetInterface(const mojo::String& interface_name, + mojo::ScopedMessagePipeHandle client_handle) override { + if (tracer_ && interface_name == tracing::TraceProvider::Name_) { + tracer_->ConnectToProvider( + mojo::MakeRequest<tracing::TraceProvider>(std::move(client_handle))); + } + } + + private: + Tracer* tracer_; + mojo::StrongBinding<mojom::InterfaceProvider> binding_; + + DISALLOW_COPY_AND_ASSIGN(TracingInterfaceProvider); +}; + +const size_t kMaxBlockingPoolThreads = 3; + +std::unique_ptr<base::Thread> CreateIOThread(const char* name) { + std::unique_ptr<base::Thread> thread(new base::Thread(name)); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + thread->StartWithOptions(options); + return thread; +} + +void OnInstanceQuit(const std::string& name, const Identity& identity) { + if (name == identity.name()) + base::MessageLoop::current()->QuitWhenIdle(); +} + +} // namespace + +Context::InitParams::InitParams() {} +Context::InitParams::~InitParams() {} + +Context::Context() + : io_thread_(CreateIOThread("io_thread")), + main_entry_time_(base::Time::Now()) {} + +Context::~Context() { + DCHECK(!base::MessageLoop::current()); + blocking_pool_->Shutdown(); +} + +// static +void Context::EnsureEmbedderIsInitialized() { + static base::LazyInstance<Setup>::Leaky setup = LAZY_INSTANCE_INITIALIZER; + setup.Get(); +} + +void Context::Init(std::unique_ptr<InitParams> init_params) { + TRACE_EVENT0("mojo_shell", "Context::Init"); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + bool trace_startup = command_line.HasSwitch(::switches::kTraceStartup); + if (trace_startup) { + tracer_.Start( + command_line.GetSwitchValueASCII(::switches::kTraceStartup), + command_line.GetSwitchValueASCII(::switches::kTraceStartupDuration), + "mojo_runner.trace"); + } + + if (!init_params || init_params->init_edk) + EnsureEmbedderIsInitialized(); + + shell_runner_ = base::MessageLoop::current()->task_runner(); + blocking_pool_ = + new base::SequencedWorkerPool(kMaxBlockingPoolThreads, "blocking_pool"); + + init_edk_ = !init_params || init_params->init_edk; + if (init_edk_) { + mojo::edk::InitIPCSupport(this, io_thread_->task_runner().get()); +#if defined(OS_MACOSX) + mojo::edk::SetMachPortProvider(MachBroker::GetInstance()->port_provider()); +#endif + } + + std::unique_ptr<NativeRunnerFactory> runner_factory; + if (command_line.HasSwitch(switches::kSingleProcess)) { +#if defined(COMPONENT_BUILD) + LOG(ERROR) << "Running Mojo in single process component build, which isn't " + << "supported because statics in apps interact. Use static build" + << " or don't pass --single-process."; +#endif + runner_factory.reset( + new InProcessNativeRunnerFactory(blocking_pool_.get())); + } else { + NativeRunnerDelegate* native_runner_delegate = init_params ? + init_params->native_runner_delegate : nullptr; + runner_factory.reset(new OutOfProcessNativeRunnerFactory( + blocking_pool_.get(), native_runner_delegate)); + } + std::unique_ptr<catalog::Store> store; + if (init_params) + store = std::move(init_params->catalog_store); + catalog_.reset( + new catalog::Catalog(blocking_pool_.get(), std::move(store), nullptr)); + shell_.reset(new Shell(std::move(runner_factory), + catalog_->TakeShellClient())); + + mojom::InterfaceProviderPtr tracing_remote_interfaces; + mojom::InterfaceProviderPtr tracing_local_interfaces; + new TracingInterfaceProvider(&tracer_, GetProxy(&tracing_local_interfaces)); + + std::unique_ptr<ConnectParams> params(new ConnectParams); + params->set_source(CreateShellIdentity()); + params->set_target(Identity("mojo:tracing", mojom::kRootUserID)); + params->set_remote_interfaces(mojo::GetProxy(&tracing_remote_interfaces)); + params->set_local_interfaces(std::move(tracing_local_interfaces)); + shell_->Connect(std::move(params)); + + if (command_line.HasSwitch(tracing::kTraceStartup)) { + tracing::TraceCollectorPtr coordinator; + auto coordinator_request = GetProxy(&coordinator); + tracing_remote_interfaces->GetInterface( + tracing::TraceCollector::Name_, coordinator_request.PassMessagePipe()); + tracer_.StartCollectingFromTracingService(std::move(coordinator)); + } + + // Record the shell startup metrics used for performance testing. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + tracing::kEnableStatsCollectionBindings)) { + tracing::StartupPerformanceDataCollectorPtr collector; + tracing_remote_interfaces->GetInterface( + tracing::StartupPerformanceDataCollector::Name_, + mojo::GetProxy(&collector).PassMessagePipe()); +#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) + // CurrentProcessInfo::CreationTime is only defined on some platforms. + const base::Time creation_time = base::CurrentProcessInfo::CreationTime(); + collector->SetShellProcessCreationTime(creation_time.ToInternalValue()); +#endif + collector->SetShellMainEntryPointTime(main_entry_time_.ToInternalValue()); + } +} + +void Context::Shutdown() { + // Actions triggered by Shell's destructor may require a current message loop, + // so we should destruct it explicitly now as ~Context() occurs post message + // loop shutdown. + shell_.reset(); + + DCHECK_EQ(base::MessageLoop::current()->task_runner(), shell_runner_); + + // If we didn't initialize the edk we should not shut it down. + if (!init_edk_) + return; + + TRACE_EVENT0("mojo_shell", "Context::Shutdown"); + // Post a task in case OnShutdownComplete is called synchronously. + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(mojo::edk::ShutdownIPCSupport)); + // We'll quit when we get OnShutdownComplete(). + base::MessageLoop::current()->Run(); +} + +void Context::OnShutdownComplete() { + DCHECK_EQ(base::MessageLoop::current()->task_runner(), shell_runner_); + base::MessageLoop::current()->QuitWhenIdle(); +} + +void Context::RunCommandLineApplication() { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + base::CommandLine::StringVector args = command_line->GetArgs(); + for (size_t i = 0; i < args.size(); ++i) { +#if defined(OS_WIN) + std::string possible_app = base::WideToUTF8(args[i]); +#else + std::string possible_app = args[i]; +#endif + if (GetNameType(possible_app) == "mojo") { + Run(possible_app); + break; + } + } +} + +void Context::Run(const std::string& name) { + shell_->SetInstanceQuitCallback(base::Bind(&OnInstanceQuit, name)); + + mojom::InterfaceProviderPtr remote_interfaces; + mojom::InterfaceProviderPtr local_interfaces; + + std::unique_ptr<ConnectParams> params(new ConnectParams); + params->set_source(CreateShellIdentity()); + params->set_target(Identity(name, mojom::kRootUserID)); + params->set_remote_interfaces(mojo::GetProxy(&remote_interfaces)); + params->set_local_interfaces(std::move(local_interfaces)); + shell_->Connect(std::move(params)); +} + +} // namespace shell diff --git a/chromium/services/shell/standalone/context.h b/chromium/services/shell/standalone/context.h new file mode 100644 index 00000000000..1d60d7ca94c --- /dev/null +++ b/chromium/services/shell/standalone/context.h @@ -0,0 +1,85 @@ +// Copyright 2013 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. + +#ifndef SERVICES_SHELL_STANDALONE_CONTEXT_H_ +#define SERVICES_SHELL_STANDALONE_CONTEXT_H_ + +#include <memory> + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "mojo/edk/embedder/process_delegate.h" +#include "services/shell/shell.h" +#include "services/shell/standalone/tracer.h" + +namespace base { +class SingleThreadTaskRunner; +} + +namespace catalog { +class Catalog; +class Store; +} + +namespace shell { +class NativeRunnerDelegate; + +// The "global" context for the shell's main process. +class Context : public mojo::edk::ProcessDelegate { + public: + struct InitParams { + InitParams(); + ~InitParams(); + + NativeRunnerDelegate* native_runner_delegate = nullptr; + std::unique_ptr<catalog::Store> catalog_store; + // If true the edk is initialized. + bool init_edk = true; + }; + + Context(); + ~Context() override; + + static void EnsureEmbedderIsInitialized(); + + // This must be called with a message loop set up for the current thread, + // which must remain alive until after Shutdown() is called. + void Init(std::unique_ptr<InitParams> init_params); + + // If Init() was called and succeeded, this must be called before destruction. + void Shutdown(); + + // Run the application specified on the command line. + void RunCommandLineApplication(); + + Shell* shell() { return shell_.get(); } + + private: + // mojo::edk::ProcessDelegate: + void OnShutdownComplete() override; + + // Runs the app specified by |name|. + void Run(const std::string& name); + + scoped_refptr<base::SingleThreadTaskRunner> shell_runner_; + std::unique_ptr<base::Thread> io_thread_; + scoped_refptr<base::SequencedWorkerPool> blocking_pool_; + + // Ensure this is destructed before task_runners_ since it owns a message pipe + // that needs the IO thread to destruct cleanly. + Tracer tracer_; + std::unique_ptr<catalog::Catalog> catalog_; + std::unique_ptr<Shell> shell_; + base::Time main_entry_time_; + bool init_edk_ = false; + + DISALLOW_COPY_AND_ASSIGN(Context); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_STANDALONE_CONTEXT_H_ diff --git a/chromium/services/shell/standalone/desktop/launcher_process.cc b/chromium/services/shell/standalone/desktop/launcher_process.cc new file mode 100644 index 00000000000..4403b227e7f --- /dev/null +++ b/chromium/services/shell/standalone/desktop/launcher_process.cc @@ -0,0 +1,57 @@ +// Copyright 2013 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 <stdio.h> +#include <string.h> + +#include <iostream> + +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/debug/stack_trace.h" +#include "base/files/file_util.h" +#include "base/i18n/icu_util.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" +#include "services/shell/standalone/context.h" +#include "services/shell/switches.h" + +namespace shell { + +int LauncherProcessMain() { +#if !defined(OFFICIAL_BUILD) + base::debug::EnableInProcessStackDumping(); +#endif + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + // http://crbug.com/546644 + command_line->AppendSwitch(switches::kNoSandbox); + + base::PlatformThread::SetName("mojo_runner"); + + // We want the Context to outlive the MessageLoop so that pipes are all + // gracefully closed / error-out before we try to shut the Context down. + Context shell_context; + { + base::MessageLoop message_loop; + CHECK(base::i18n::InitializeICU()); + shell_context.Init(nullptr); + + message_loop.PostTask( + FROM_HERE, + base::Bind(&Context::RunCommandLineApplication, + base::Unretained(&shell_context))); + + message_loop.Run(); + + // Must be called before |message_loop| is destroyed. + shell_context.Shutdown(); + } + + return 0; +} + +} // namespace shell diff --git a/chromium/services/shell/standalone/desktop/launcher_process.h b/chromium/services/shell/standalone/desktop/launcher_process.h new file mode 100644 index 00000000000..13cdf5194f0 --- /dev/null +++ b/chromium/services/shell/standalone/desktop/launcher_process.h @@ -0,0 +1,17 @@ +// 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. + +#ifndef SERVICES_SHELL_STANDALONE_LAUNCHER_PROCESS_H_ +#define SERVICES_SHELL_STANDALONE_LAUNCHER_PROCESS_H_ + +#include "base/callback_forward.h" + +namespace shell { + +// Main method for the launcher process. +int LauncherProcessMain(); + +} // namespace shell + +#endif // SERVICES_SHELL_STANDALONE_LAUNCHER_PROCESS_H_ diff --git a/chromium/services/shell/standalone/desktop/main.cc b/chromium/services/shell/standalone/desktop/main.cc new file mode 100644 index 00000000000..9a27ab2844c --- /dev/null +++ b/chromium/services/shell/standalone/desktop/main.cc @@ -0,0 +1,9 @@ +// Copyright 2013 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 "services/shell/standalone/desktop/main_helper.h" + +int main(int argc, char** argv) { + return shell::StandaloneShellMain(argc, argv); +} diff --git a/chromium/services/shell/standalone/desktop/main_helper.cc b/chromium/services/shell/standalone/desktop/main_helper.cc new file mode 100644 index 00000000000..d51f421c1d8 --- /dev/null +++ b/chromium/services/shell/standalone/desktop/main_helper.cc @@ -0,0 +1,49 @@ +// 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 "services/shell/standalone/desktop/main_helper.h" + +#include "base/at_exit.h" +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug/debugger.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/process/launch.h" +#include "base/stl_util.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "services/shell/runner/common/switches.h" +#include "services/shell/runner/host/child_process.h" +#include "services/shell/runner/init.h" +#include "services/shell/standalone/desktop/launcher_process.h" + +#if defined(OS_WIN) +#include <windows.h> +#elif (OS_POSIX) +#include <unistd.h> +#endif + +namespace shell { + +int StandaloneShellMain(int argc, char** argv) { + base::CommandLine::Init(argc, argv); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + base::AtExitManager at_exit; + InitializeLogging(); + WaitForDebuggerIfNecessary(); + +#if !defined(OFFICIAL_BUILD) && defined(OS_WIN) + base::RouteStdioToConsole(false); +#endif + + if (command_line.HasSwitch(switches::kChildProcess)) + return ChildProcessMain(); + + return LauncherProcessMain(); +} + +} // namespace shell diff --git a/chromium/services/shell/standalone/desktop/main_helper.h b/chromium/services/shell/standalone/desktop/main_helper.h new file mode 100644 index 00000000000..f326957d328 --- /dev/null +++ b/chromium/services/shell/standalone/desktop/main_helper.h @@ -0,0 +1,15 @@ +// 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. + +#ifndef SERVICES_SHELL_STANDALONE_DESKTOP_MAIN_HELPER_H +#define SERVICES_SHELL_STANDALONE_DESKTOP_MAIN_HELPER_H + +namespace shell { + +// Helper method to start Mojo standalone shell code. +int StandaloneShellMain(int argc, char** argv); + +} // namespace shell + +#endif // SERVICES_SHELL_STANDALONE_DESKTOP_MAIN_HELPER_H diff --git a/chromium/services/shell/standalone/tracer.cc b/chromium/services/shell/standalone/tracer.cc new file mode 100644 index 00000000000..cffcfe09c55 --- /dev/null +++ b/chromium/services/shell/standalone/tracer.cc @@ -0,0 +1,162 @@ +// 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 "services/shell/standalone/tracer.h" + +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <utility> + +#include "base/message_loop/message_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/trace_event/trace_config.h" +#include "base/trace_event/trace_event.h" + +namespace shell { + +Tracer::Tracer() + : tracing_(false), first_chunk_written_(false), trace_file_(nullptr) {} + +Tracer::~Tracer() { + StopAndFlushToFile(); +} + +void Tracer::Start(const std::string& categories, + const std::string& duration_seconds_str, + const std::string& filename) { + tracing_ = true; + trace_filename_ = filename; + categories_ = categories; + base::trace_event::TraceConfig config(categories, + base::trace_event::RECORD_UNTIL_FULL); + base::trace_event::TraceLog::GetInstance()->SetEnabled( + config, base::trace_event::TraceLog::RECORDING_MODE); + + int trace_duration_secs = 5; + if (!duration_seconds_str.empty()) { + CHECK(base::StringToInt(duration_seconds_str, &trace_duration_secs)) + << "Could not parse --trace-startup-duration value " + << duration_seconds_str; + } + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&Tracer::StopAndFlushToFile, base::Unretained(this)), + base::TimeDelta::FromSeconds(trace_duration_secs)); +} + +void Tracer::StartCollectingFromTracingService( + tracing::TraceCollectorPtr coordinator) { + coordinator_ = std::move(coordinator); + mojo::DataPipe data_pipe; + coordinator_->Start(std::move(data_pipe.producer_handle), categories_); + drainer_.reset(new mojo::common::DataPipeDrainer( + this, std::move(data_pipe.consumer_handle))); +} + +void Tracer::StopAndFlushToFile() { + if (tracing_) + StopTracingAndFlushToDisk(); +} + +void Tracer::ConnectToProvider( + mojo::InterfaceRequest<tracing::TraceProvider> request) { + trace_provider_impl_.Bind(std::move(request)); +} + +void Tracer::StopTracingAndFlushToDisk() { + tracing_ = false; + trace_file_ = fopen(trace_filename_.c_str(), "w+"); + PCHECK(trace_file_); + static const char kStart[] = "{\"traceEvents\":["; + PCHECK(fwrite(kStart, 1, strlen(kStart), trace_file_) == strlen(kStart)); + + // At this point we might be connected to the tracing service, in which case + // we want to tell it to stop tracing and we will send the data we've + // collected in process to it. + if (coordinator_) { + coordinator_->StopAndFlush(); + } else { + // Or we might not be connected. If we aren't connected to the tracing + // service we want to collect the tracing data gathered ourselves and flush + // it to disk. We do this in a blocking fashion (for this thread) so we can + // gather as much data as possible on shutdown. + base::trace_event::TraceLog::GetInstance()->SetDisabled(); + { + base::WaitableEvent flush_complete_event(false, false); + // TraceLog::Flush requires a message loop but we've already shut ours + // down. + // Spin up a new thread to flush things out. + base::Thread flush_thread("mojo_runner_trace_event_flush"); + flush_thread.Start(); + flush_thread.message_loop()->PostTask( + FROM_HERE, + base::Bind(&Tracer::EndTraceAndFlush, base::Unretained(this), + trace_filename_, + base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&flush_complete_event)))); + base::trace_event::TraceLog::GetInstance() + ->SetCurrentThreadBlocksMessageLoop(); + flush_complete_event.Wait(); + } + } +} + +void Tracer::WriteFooterAndClose() { + static const char kEnd[] = "]}"; + PCHECK(fwrite(kEnd, 1, strlen(kEnd), trace_file_) == strlen(kEnd)); + PCHECK(fclose(trace_file_) == 0); + trace_file_ = nullptr; + LOG(ERROR) << "Wrote trace data to " << trace_filename_; +} + +void Tracer::EndTraceAndFlush(const std::string& filename, + const base::Closure& done_callback) { + base::trace_event::TraceLog::GetInstance()->SetDisabled(); + base::trace_event::TraceLog::GetInstance()->Flush(base::Bind( + &Tracer::WriteTraceDataCollected, base::Unretained(this), done_callback)); +} + +void Tracer::WriteTraceDataCollected( + const base::Closure& done_callback, + const scoped_refptr<base::RefCountedString>& events_str, + bool has_more_events) { + if (events_str->size()) { + WriteCommaIfNeeded(); + + PCHECK(fwrite(events_str->data().c_str(), 1, events_str->data().length(), + trace_file_) == events_str->data().length()); + } + + if (!has_more_events && !done_callback.is_null()) + done_callback.Run(); +} + +void Tracer::OnDataAvailable(const void* data, size_t num_bytes) { + const char* chars = static_cast<const char*>(data); + trace_service_data_.append(chars, num_bytes); +} + +void Tracer::OnDataComplete() { + if (!trace_service_data_.empty()) { + WriteCommaIfNeeded(); + const char* const chars = trace_service_data_.data(); + size_t num_bytes = trace_service_data_.length(); + PCHECK(fwrite(chars, 1, num_bytes, trace_file_) == num_bytes); + trace_service_data_ = std::string(); + } + drainer_.reset(); + coordinator_.reset(); + WriteFooterAndClose(); +} + +void Tracer::WriteCommaIfNeeded() { + if (first_chunk_written_) + PCHECK(fwrite(",", 1, 1, trace_file_) == 1); + first_chunk_written_ = true; +} + +} // namespace shell diff --git a/chromium/services/shell/standalone/tracer.h b/chromium/services/shell/standalone/tracer.h new file mode 100644 index 00000000000..173f3ab2ddb --- /dev/null +++ b/chromium/services/shell/standalone/tracer.h @@ -0,0 +1,101 @@ +// 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. + +#ifndef SERVICES_SHELL_STANDALONE_TRACER_H_ +#define SERVICES_SHELL_STANDALONE_TRACER_H_ + +#include <stddef.h> +#include <stdio.h> + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted_memory.h" +#include "mojo/common/data_pipe_drainer.h" +#include "services/tracing/public/cpp/trace_provider_impl.h" +#include "services/tracing/public/interfaces/tracing.mojom.h" + +namespace shell { + +// Tracer collects tracing data from base/trace_event and from externally +// configured sources, aggregates it into a single stream, and writes it out to +// a file. It should be constructed very early in a process' lifetime before any +// initialization that may be interesting to trace has occured and be shut down +// as late as possible to capture as much initialization/shutdown code as +// possible. +class Tracer : public mojo::common::DataPipeDrainer::Client { + public: + Tracer(); + ~Tracer() override; + + // Starts tracing the current process with the given set of categories. The + // tracing results will be saved into the specified filename when + // StopAndFlushToFile() is called. + void Start(const std::string& categories, + const std::string& duration_seconds_str, + const std::string& filename); + + // Starts collecting data from the tracing service with the given set of + // categories. + void StartCollectingFromTracingService( + tracing::TraceCollectorPtr coordinator); + + // Stops tracing and flushes all collected trace data to the file specified in + // Start(). Blocks until the file write is complete. May be called after the + // message loop is shut down. + void StopAndFlushToFile(); + + void ConnectToProvider( + mojo::InterfaceRequest<tracing::TraceProvider> request); + + private: + void StopTracingAndFlushToDisk(); + + // Called from the flush thread. When all data is collected this runs + // |done_callback| on the flush thread. + void EndTraceAndFlush(const std::string& filename, + const base::Closure& done_callback); + + // Called from the flush thread. + void WriteTraceDataCollected( + const base::Closure& done_callback, + const scoped_refptr<base::RefCountedString>& events_str, + bool has_more_events); + + // mojo::common::DataPipeDrainer::Client implementation. + void OnDataAvailable(const void* data, size_t num_bytes) override; + void OnDataComplete() override; + + // Emits a comma if needed. + void WriteCommaIfNeeded(); + + // Writes trace file footer and closes out the file. + void WriteFooterAndClose(); + + // Set when connected to the tracing service. + tracing::TraceCollectorPtr coordinator_; + std::unique_ptr<mojo::common::DataPipeDrainer> drainer_; + + mojo::TraceProviderImpl trace_provider_impl_; + // Whether we're currently tracing. + bool tracing_; + // Categories to trace. + std::string categories_; + + // Whether we've written the first chunk. + bool first_chunk_written_; + std::string trace_service_data_; + + // Trace file, if open. + FILE* trace_file_; + std::string trace_filename_; + + DISALLOW_COPY_AND_ASSIGN(Tracer); +}; + +} // namespace shell + +#endif // SERVICES_SHELL_STANDALONE_TRACER_H_ diff --git a/chromium/services/shell/switches.cc b/chromium/services/shell/switches.cc new file mode 100644 index 00000000000..10dc430c04d --- /dev/null +++ b/chromium/services/shell/switches.cc @@ -0,0 +1,17 @@ +// Copyright 2013 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 "services/shell/switches.h" + +namespace shell { +namespace switches { + +// Disables the sandbox for debugging. +const char kNoSandbox[] = "no-sandbox"; + +// Load apps in a single processes. +const char kSingleProcess[] = "single-process"; + +} // namespace switches +} // namespace shell diff --git a/chromium/services/shell/switches.h b/chromium/services/shell/switches.h new file mode 100644 index 00000000000..1a442e172f4 --- /dev/null +++ b/chromium/services/shell/switches.h @@ -0,0 +1,22 @@ +// Copyright 2013 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. + +#ifndef SERVICES_SHELL_SWITCHES_H_ +#define SERVICES_SHELL_SWITCHES_H_ + +#include <set> +#include <string> + +namespace shell { +namespace switches { + +// All switches in alphabetical order. The switches should be documented +// alongside the definition of their values in the .cc file. +extern const char kNoSandbox[]; +extern const char kSingleProcess[]; + +} // namespace switches +} // namespace shell + +#endif // SERVICES_SHELL_SWITCHES_H_ diff --git a/chromium/services/tracing/BUILD.gn b/chromium/services/tracing/BUILD.gn new file mode 100644 index 00000000000..e63d3700d81 --- /dev/null +++ b/chromium/services/tracing/BUILD.gn @@ -0,0 +1,47 @@ +# Copyright 2014 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. + +import("//mojo/public/mojo_application.gni") +import("//mojo/public/mojo_application_manifest.gni") + +mojo_native_application("tracing") { + sources = [ + "main.cc", + ] + + avoid_runner_cycle = true + + deps = [ + ":lib", + "//mojo/public/cpp/system", + "//services/shell/public/cpp", + ] + + data_deps = [ + ":manifest", + ] +} + +mojo_application_manifest("manifest") { + application_name = "tracing" + source = "manifest.json" +} + +source_set("lib") { + sources = [ + "trace_data_sink.cc", + "trace_data_sink.h", + "trace_recorder_impl.cc", + "trace_recorder_impl.h", + "tracing_app.cc", + "tracing_app.h", + ] + + deps = [ + "//base", + "//mojo/common:common_base", + "//services/shell/public/cpp", + "//services/tracing/public/interfaces", + ] +} diff --git a/chromium/services/tracing/OWNERS b/chromium/services/tracing/OWNERS new file mode 100644 index 00000000000..4733a4f06bf --- /dev/null +++ b/chromium/services/tracing/OWNERS @@ -0,0 +1 @@ +erg@chromium.org diff --git a/chromium/services/tracing/main.cc b/chromium/services/tracing/main.cc new file mode 100644 index 00000000000..758efedf985 --- /dev/null +++ b/chromium/services/tracing/main.cc @@ -0,0 +1,12 @@ +// Copyright 2014 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/public/c/system/main.h" +#include "services/shell/public/cpp/application_runner.h" +#include "services/tracing/tracing_app.h" + +MojoResult MojoMain(MojoHandle shell_handle) { + shell::ApplicationRunner runner(new tracing::TracingApp); + return runner.Run(shell_handle); +} diff --git a/chromium/services/tracing/manifest.json b/chromium/services/tracing/manifest.json new file mode 100644 index 00000000000..a8197395b2b --- /dev/null +++ b/chromium/services/tracing/manifest.json @@ -0,0 +1,16 @@ +{ + "manifest_version": 1, + "name": "mojo:tracing", + "display_name": "Tracing Collector", + "capabilities": { + "provided": { + "app": [ + "tracing::TracingCollector", + "tracing::StartupPerformanceDataCollector" + ] + }, + "required": { + "mojo:shell": { "classes": [ "shell:all_users" ] } + } + } +} diff --git a/chromium/services/tracing/public/cpp/BUILD.gn b/chromium/services/tracing/public/cpp/BUILD.gn new file mode 100644 index 00000000000..7fa6eab5d02 --- /dev/null +++ b/chromium/services/tracing/public/cpp/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +source_set("cpp") { + sources = [ + "switches.cc", + "switches.h", + "trace_provider_impl.cc", + "trace_provider_impl.h", + "tracing_impl.cc", + "tracing_impl.h", + ] + + deps = [ + "//base", + "//mojo/public/cpp/bindings", + "//services/shell/public/cpp", + "//services/tracing/public/interfaces", + ] +} diff --git a/chromium/services/tracing/public/cpp/switches.cc b/chromium/services/tracing/public/cpp/switches.cc new file mode 100644 index 00000000000..279c890708d --- /dev/null +++ b/chromium/services/tracing/public/cpp/switches.cc @@ -0,0 +1,22 @@ +// 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 "services/tracing/public/cpp/switches.h" + +namespace tracing { + +// Specifies if the |StatsCollectionController| needs to be bound in html pages. +// This binding happens on per-frame basis and hence can potentially be a +// performance bottleneck. One should only enable it when running a test that +// needs to access the provided statistics. +const char kEnableStatsCollectionBindings[] = + "enable-stats-collection-bindings"; + +const char kTraceStartup[] = "trace-startup"; + +#ifdef NDEBUG +const char kEarlyTracing[] = "early-tracing"; +#endif + +} // namespace tracing diff --git a/chromium/services/tracing/public/cpp/switches.h b/chromium/services/tracing/public/cpp/switches.h new file mode 100644 index 00000000000..b127d566a85 --- /dev/null +++ b/chromium/services/tracing/public/cpp/switches.h @@ -0,0 +1,24 @@ +// 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. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_SWITCHES_H_ +#define SERVICES_TRACING_PUBLIC_CPP_SWITCHES_H_ + +namespace tracing { + +// All switches in alphabetical order. The switches should be documented +// alongside the definition of their values in the .cc file. +extern const char kEnableStatsCollectionBindings[]; + +extern const char kTraceStartup[]; + +#ifdef NDEBUG +// In release builds, specifying this flag will force reporting of tracing +// before the main Application is initialized. +extern const char kEarlyTracing[]; +#endif + +} // namespace tracing + +#endif // SERVICES_TRACING_PUBLIC_CPP_SWITCHES_H_ diff --git a/chromium/services/tracing/public/cpp/trace_provider_impl.cc b/chromium/services/tracing/public/cpp/trace_provider_impl.cc new file mode 100644 index 00000000000..08a80110869 --- /dev/null +++ b/chromium/services/tracing/public/cpp/trace_provider_impl.cc @@ -0,0 +1,102 @@ +// 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 "services/tracing/public/cpp/trace_provider_impl.h" + +#include <utility> + +#include "base/callback.h" +#include "base/logging.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "base/trace_event/trace_config.h" +#include "base/trace_event/trace_event.h" +#include "services/shell/public/cpp/connection.h" + +namespace mojo { + +TraceProviderImpl::TraceProviderImpl() + : binding_(this), tracing_forced_(false), weak_factory_(this) {} + +TraceProviderImpl::~TraceProviderImpl() { + StopTracing(); +} + +void TraceProviderImpl::Bind(InterfaceRequest<tracing::TraceProvider> request) { + if (!binding_.is_bound()) { + binding_.Bind(std::move(request)); + } else { + LOG(ERROR) << "Cannot accept two connections to TraceProvider."; + } +} + +void TraceProviderImpl::StartTracing(const String& categories, + tracing::TraceRecorderPtr recorder) { + DCHECK(!recorder_); + recorder_ = std::move(recorder); + tracing_forced_ = false; + if (!base::trace_event::TraceLog::GetInstance()->IsEnabled()) { + std::string categories_str = categories.To<std::string>(); + base::trace_event::TraceLog::GetInstance()->SetEnabled( + base::trace_event::TraceConfig(categories_str, + base::trace_event::RECORD_UNTIL_FULL), + base::trace_event::TraceLog::RECORDING_MODE); + } +} + +void TraceProviderImpl::StopTracing() { + if (recorder_) { + base::trace_event::TraceLog::GetInstance()->SetDisabled(); + + base::trace_event::TraceLog::GetInstance()->Flush( + base::Bind(&TraceProviderImpl::SendChunk, base::Unretained(this))); + } +} + +void TraceProviderImpl::ForceEnableTracing() { + base::trace_event::TraceLog::GetInstance()->SetEnabled( + base::trace_event::TraceConfig("*", base::trace_event::RECORD_UNTIL_FULL), + base::trace_event::TraceLog::RECORDING_MODE); + tracing_forced_ = true; + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&TraceProviderImpl::DelayedStop, weak_factory_.GetWeakPtr())); +} + +void TraceProviderImpl::DelayedStop() { + // We use this indirection to account for cases where the Initialize app + // method (within which TraceProviderImpl is created) takes more than one + // second to finish; thus we start the countdown only when the current thread + // is unblocked. + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&TraceProviderImpl::StopIfForced, weak_factory_.GetWeakPtr()), + base::TimeDelta::FromSeconds(1)); +} + +void TraceProviderImpl::StopIfForced() { + if (!tracing_forced_) { + return; + } + base::trace_event::TraceLog::GetInstance()->SetDisabled(); + base::trace_event::TraceLog::GetInstance()->Flush( + base::Callback<void(const scoped_refptr<base::RefCountedString>&, + bool)>()); +} + +void TraceProviderImpl::SendChunk( + const scoped_refptr<base::RefCountedString>& events_str, + bool has_more_events) { + DCHECK(recorder_); + // The string will be empty if an error eccured or there were no trace + // events. Empty string is not a valid chunk to record so skip in this case. + if (!events_str->data().empty()) { + recorder_->Record(mojo::String(events_str->data())); + } + if (!has_more_events) { + recorder_.reset(); + } +} + +} // namespace mojo diff --git a/chromium/services/tracing/public/cpp/trace_provider_impl.h b/chromium/services/tracing/public/cpp/trace_provider_impl.h new file mode 100644 index 00000000000..975781b4a2e --- /dev/null +++ b/chromium/services/tracing/public/cpp/trace_provider_impl.h @@ -0,0 +1,51 @@ +// 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. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_TRACE_PROVIDER_IMPL_H_ +#define SERVICES_TRACING_PUBLIC_CPP_TRACE_PROVIDER_IMPL_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/tracing/public/interfaces/tracing.mojom.h" + +namespace mojo { + +class TraceProviderImpl : public tracing::TraceProvider { + public: + TraceProviderImpl(); + ~TraceProviderImpl() override; + + void Bind(InterfaceRequest<tracing::TraceProvider> request); + + // Enable tracing without waiting for an inbound connection. It will stop if + // no TraceRecorder is sent within a set time. + void ForceEnableTracing(); + + private: + // tracing::TraceProvider implementation: + void StartTracing(const String& categories, + tracing::TraceRecorderPtr recorder) override; + void StopTracing() override; + + void SendChunk(const scoped_refptr<base::RefCountedString>& events_str, + bool has_more_events); + + void DelayedStop(); + // Stop the collection of traces if no external connection asked for them yet. + void StopIfForced(); + + Binding<tracing::TraceProvider> binding_; + bool tracing_forced_; + tracing::TraceRecorderPtr recorder_; + + base::WeakPtrFactory<TraceProviderImpl> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(TraceProviderImpl); +}; + +} // namespace mojo + +#endif // SERVICES_TRACING_PUBLIC_CPP_TRACE_PROVIDER_IMPL_H_ diff --git a/chromium/services/tracing/public/cpp/tracing_impl.cc b/chromium/services/tracing/public/cpp/tracing_impl.cc new file mode 100644 index 00000000000..6b6f68fb64f --- /dev/null +++ b/chromium/services/tracing/public/cpp/tracing_impl.cc @@ -0,0 +1,75 @@ +// Copyright 2014 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 "services/tracing/public/cpp/tracing_impl.h" + +#include <utility> + +#include "base/lazy_instance.h" +#include "base/synchronization/lock.h" +#include "base/threading/platform_thread.h" +#include "base/trace_event/trace_event_impl.h" +#include "services/shell/public/cpp/connector.h" + +#ifdef NDEBUG +#include "base/command_line.h" +#include "services/tracing/public/cpp/switches.h" +#endif + +namespace mojo { +namespace { + +// Controls access to |g_tracing_singleton_created|, which can be accessed from +// different threads. +base::LazyInstance<base::Lock>::Leaky g_singleton_lock = + LAZY_INSTANCE_INITIALIZER; + +// Whether we are the first TracingImpl to be created in this mojo +// application. The first TracingImpl in a physical mojo application connects +// to the mojo:tracing service. +// +// If this is a ContentHandler, it will outlive all its served Applications. If +// this is a raw mojo application, it is the only Application served. +bool g_tracing_singleton_created = false; + +} + +TracingImpl::TracingImpl() { +} + +TracingImpl::~TracingImpl() { +} + +void TracingImpl::Initialize(shell::Connector* connector, + const std::string& url) { + { + base::AutoLock lock(g_singleton_lock.Get()); + if (g_tracing_singleton_created) + return; + g_tracing_singleton_created = true; + } + + // This will only set the name for the first app in a loaded mojo file. It's + // up to something like CoreServices to name its own child threads. + base::PlatformThread::SetName(url); + + connection_ = connector->Connect("mojo:tracing"); + connection_->AddInterface(this); + +#ifdef NDEBUG + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + tracing::kEarlyTracing)) { + provider_impl_.ForceEnableTracing(); + } +#else + provider_impl_.ForceEnableTracing(); +#endif +} + +void TracingImpl::Create(shell::Connection* connection, + InterfaceRequest<tracing::TraceProvider> request) { + provider_impl_.Bind(std::move(request)); +} + +} // namespace mojo diff --git a/chromium/services/tracing/public/cpp/tracing_impl.h b/chromium/services/tracing/public/cpp/tracing_impl.h new file mode 100644 index 00000000000..ed780e5ee42 --- /dev/null +++ b/chromium/services/tracing/public/cpp/tracing_impl.h @@ -0,0 +1,52 @@ +// Copyright 2014 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. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_TRACING_IMPL_H_ +#define SERVICES_TRACING_PUBLIC_CPP_TRACING_IMPL_H_ + +#include "base/macros.h" +#include "services/shell/public/cpp/interface_factory.h" +#include "services/tracing/public/cpp/trace_provider_impl.h" +#include "services/tracing/public/interfaces/tracing.mojom.h" + +namespace shell { +class Connection; +class Connector; +} + +namespace mojo { + +// Connects to mojo:tracing during your Application's Initialize() call once +// per process. +// +// We need to deal with multiple ways of packaging mojo applications +// together. We'll need to deal with packages that use the mojo.ContentHandler +// interface to bundle several Applciations into a single physical on disk +// mojo binary, and with those same services each in their own mojo binary. +// +// Have your bundle ContentHandler own a TracingImpl, and each Application own +// a TracingImpl. In bundles, the second TracingImpl will be a no-op. +class TracingImpl : public shell::InterfaceFactory<tracing::TraceProvider> { + public: + TracingImpl(); + ~TracingImpl() override; + + // This connects to the tracing service and registers ourselves to provide + // tracing data on demand. + void Initialize(shell::Connector* connector, const std::string& url); + + private: + // InterfaceFactory<tracing::TraceProvider> implementation. + void Create(shell::Connection* connection, + InterfaceRequest<tracing::TraceProvider> request) override; + + std::unique_ptr<shell::Connection> connection_; + TraceProviderImpl provider_impl_; + + DISALLOW_COPY_AND_ASSIGN(TracingImpl); +}; + +} // namespace mojo + +#endif // SERVICES_TRACING_PUBLIC_CPP_TRACING_IMPL_H_ diff --git a/chromium/services/tracing/public/interfaces/BUILD.gn b/chromium/services/tracing/public/interfaces/BUILD.gn new file mode 100644 index 00000000000..8323805231a --- /dev/null +++ b/chromium/services/tracing/public/interfaces/BUILD.gn @@ -0,0 +1,11 @@ +# 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { + sources = [ + "tracing.mojom", + ] +} diff --git a/chromium/services/tracing/public/interfaces/OWNERS b/chromium/services/tracing/public/interfaces/OWNERS new file mode 100644 index 00000000000..9e621796752 --- /dev/null +++ b/chromium/services/tracing/public/interfaces/OWNERS @@ -0,0 +1,13 @@ +# Changes to Mojo interfaces require a security review to avoid +# introducing new sandbox escapes. +per-file *.mojom=set noparent +per-file *.mojom=dcheng@chromium.org +per-file *.mojom=inferno@chromium.org +per-file *.mojom=jln@chromium.org +per-file *.mojom=jschuh@chromium.org +per-file *.mojom=kenrb@chromium.org +per-file *.mojom=mkwst@chromium.org +per-file *.mojom=nasko@chromium.org +per-file *.mojom=palmer@chromium.org +per-file *.mojom=tsepez@chromium.org +per-file *.mojom=wfh@chromium.org diff --git a/chromium/services/tracing/public/interfaces/tracing.mojom b/chromium/services/tracing/public/interfaces/tracing.mojom new file mode 100644 index 00000000000..b624f81e24f --- /dev/null +++ b/chromium/services/tracing/public/interfaces/tracing.mojom @@ -0,0 +1,62 @@ +// Copyright 2014 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. + +module tracing; + +// To participate in the tracing ecosystem, implement the TraceProvider +// interface and connect to the tracing app. Then, when the provider's Start() +// function is called collect tracing data and pass it back via the provided +// TraceRecorder interface up until Stop() is called. + +interface TraceProvider { + // Categories can either be the empty string to mean the default set of + // categories or a comma-delimited list of categories to trace. + StartTracing(string categories, TraceRecorder recorder); + StopTracing(); +}; + +interface TraceRecorder { + Record(string json); +}; + +interface TraceCollector { + // Request tracing data from all connected providers to stream to + // |stream|. + Start(handle<data_pipe_producer> stream, string categories); + + // Stop tracing and flush results to the |stream| passed in to Start(). + // Closes |stream| when all data is collected. + StopAndFlush(); +}; + +// These times are used to determine startup performance metrics. +// TODO(msw): Find a way to convert *_time metrics into TimeTicks earlier (ref: +// https://goo.gl/vZ8dZW). +struct StartupPerformanceTimes { + // TODO(msw): Rename to match "BrowserMainEntryTimeAbsolute" metric? + int64 shell_process_creation_time; + int64 shell_main_entry_point_time; + int64 browser_message_loop_start_ticks; + int64 browser_window_display_ticks; + int64 browser_open_tabs_time_delta; + // TODO(msw): Rename to avoid "web contents"? + int64 first_web_contents_main_frame_load_ticks; + // TODO(msw): Rename to match "FirstWebContents.NonEmptyPaint" metric? + int64 first_visually_non_empty_layout_ticks; +}; + +// This interface accepts startup performance timing from a variety of sources. +interface StartupPerformanceDataCollector { + // These setters may be called many times, only the first time is recorded. + SetShellProcessCreationTime(int64 time); + SetShellMainEntryPointTime(int64 time); + SetBrowserMessageLoopStartTicks(int64 ticks); + SetBrowserWindowDisplayTicks(int64 ticks); + SetBrowserOpenTabsTimeDelta(int64 delta); + SetFirstWebContentsMainFrameLoadTicks(int64 ticks); + SetFirstVisuallyNonEmptyLayoutTicks(int64 ticks); + + // Get the currently available startup performance times. + GetStartupPerformanceTimes() => (StartupPerformanceTimes times); +}; diff --git a/chromium/services/tracing/trace_data_sink.cc b/chromium/services/tracing/trace_data_sink.cc new file mode 100644 index 00000000000..b278956648b --- /dev/null +++ b/chromium/services/tracing/trace_data_sink.cc @@ -0,0 +1,32 @@ +// Copyright 2014 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 "services/tracing/trace_data_sink.h" + +#include <utility> + +#include "base/logging.h" +#include "mojo/common/data_pipe_utils.h" + +using mojo::common::BlockingCopyFromString; + +namespace tracing { + +TraceDataSink::TraceDataSink(mojo::ScopedDataPipeProducerHandle pipe) + : pipe_(std::move(pipe)), empty_(true) {} + +TraceDataSink::~TraceDataSink() { + if (pipe_.is_valid()) + pipe_.reset(); + DCHECK(!pipe_.is_valid()); +} + +void TraceDataSink::AddChunk(const std::string& json) { + if (!empty_) + BlockingCopyFromString(",", pipe_); + empty_ = false; + BlockingCopyFromString(json, pipe_); +} + +} // namespace tracing diff --git a/chromium/services/tracing/trace_data_sink.h b/chromium/services/tracing/trace_data_sink.h new file mode 100644 index 00000000000..6862faa08ba --- /dev/null +++ b/chromium/services/tracing/trace_data_sink.h @@ -0,0 +1,31 @@ +// Copyright 2014 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. + +#ifndef SERVICES_TRACING_TRACE_DATA_SINK_H_ +#define SERVICES_TRACING_TRACE_DATA_SINK_H_ + +#include <string> + +#include "base/macros.h" +#include "mojo/public/cpp/system/data_pipe.h" + +namespace tracing { + +class TraceDataSink { + public: + explicit TraceDataSink(mojo::ScopedDataPipeProducerHandle pipe); + ~TraceDataSink(); + + void AddChunk(const std::string& json); + + private: + mojo::ScopedDataPipeProducerHandle pipe_; + bool empty_; + + DISALLOW_COPY_AND_ASSIGN(TraceDataSink); +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_TRACE_DATA_SINK_H_ diff --git a/chromium/services/tracing/trace_recorder_impl.cc b/chromium/services/tracing/trace_recorder_impl.cc new file mode 100644 index 00000000000..f7057929bac --- /dev/null +++ b/chromium/services/tracing/trace_recorder_impl.cc @@ -0,0 +1,31 @@ +// 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 "services/tracing/trace_recorder_impl.h" + +#include <utility> + +namespace tracing { + +TraceRecorderImpl::TraceRecorderImpl( + mojo::InterfaceRequest<TraceRecorder> request, + TraceDataSink* sink) + : sink_(sink), binding_(this, std::move(request)) {} + +TraceRecorderImpl::~TraceRecorderImpl() { +} + +void TraceRecorderImpl::TryRead() { + binding_.WaitForIncomingMethodCall(MojoDeadline(0)); +} + +mojo::Handle TraceRecorderImpl::TraceRecorderHandle() const { + return binding_.handle(); +} + +void TraceRecorderImpl::Record(const mojo::String& json) { + sink_->AddChunk(json.To<std::string>()); +} + +} // namespace tracing diff --git a/chromium/services/tracing/trace_recorder_impl.h b/chromium/services/tracing/trace_recorder_impl.h new file mode 100644 index 00000000000..2e63e31ce88 --- /dev/null +++ b/chromium/services/tracing/trace_recorder_impl.h @@ -0,0 +1,43 @@ +// 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. + +#ifndef SERVICES_TRACING_TRACE_RECORDER_IMPL_H_ +#define SERVICES_TRACING_TRACE_RECORDER_IMPL_H_ + +#include "base/macros.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/string.h" +#include "services/tracing/public/interfaces/tracing.mojom.h" +#include "services/tracing/trace_data_sink.h" + +namespace tracing { + +class TraceRecorderImpl : public TraceRecorder { + public: + TraceRecorderImpl(mojo::InterfaceRequest<TraceRecorder> request, + TraceDataSink* sink); + ~TraceRecorderImpl() override; + + // TryRead attempts to read a single chunk from the TraceRecorder pipe if one + // is available and passes it to the TraceDataSink. Returns immediately if no + // chunk is available. + void TryRead(); + + // TraceRecorderHandle returns the handle value of the TraceRecorder + // binding which can be used to wait until chunks are available. + mojo::Handle TraceRecorderHandle() const; + + private: + // tracing::TraceRecorder implementation. + void Record(const mojo::String& json) override; + + TraceDataSink* sink_; + mojo::Binding<TraceRecorder> binding_; + + DISALLOW_COPY_AND_ASSIGN(TraceRecorderImpl); +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_TRACE_RECORDER_IMPL_H_ diff --git a/chromium/services/tracing/tracing.gyp b/chromium/services/tracing/tracing.gyp new file mode 100644 index 00000000000..38ff437c71f --- /dev/null +++ b/chromium/services/tracing/tracing.gyp @@ -0,0 +1,61 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'tracing_interfaces', + 'type': 'none', + 'variables': { + 'mojom_files': [ + 'public/interfaces/tracing.mojom', + ], + 'mojom_include_path': '<(DEPTH)/mojo/services', + }, + 'includes': [ + '../../mojo/mojom_bindings_generator_explicit.gypi', + ], + }, + { + # Technically, these should be in the mojo_services.gyp, but this causes + # a cycle since the ios generator can't have gyp files refer to each + # other, even if the targets don't form a cycle. + # + # GN version: //services/tracing:lib + 'target_name': 'tracing_lib', + 'type': 'static_library', + 'dependencies': [ + 'tracing_public', + '../../mojo/mojo_edk.gyp:mojo_system_impl', + '../shell/shell_public.gyp:shell_public', + ], + 'sources': [ + 'trace_data_sink.cc', + 'trace_data_sink.h', + 'trace_recorder_impl.cc', + 'trace_recorder_impl.h', + 'tracing_app.cc', + 'tracing_app.h', + ], + }, + { + # GN version: //mojo/services/public/cpp + 'target_name': 'tracing_public', + 'type': 'static_library', + 'dependencies': [ + 'tracing_interfaces', + '../../mojo/mojo_edk.gyp:mojo_system_impl', + '../shell/shell_public.gyp:shell_public', + ], + 'sources': [ + 'public/cpp/switches.cc', + 'public/cpp/switches.h', + 'public/cpp/tracing_impl.cc', + 'public/cpp/tracing_impl.h', + 'public/cpp/trace_provider_impl.cc', + 'public/cpp/trace_provider_impl.h', + ], + }, + ], +} diff --git a/chromium/services/tracing/tracing_app.cc b/chromium/services/tracing/tracing_app.cc new file mode 100644 index 00000000000..2dff117753a --- /dev/null +++ b/chromium/services/tracing/tracing_app.cc @@ -0,0 +1,186 @@ +// 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 "services/tracing/tracing_app.h" + +#include <stddef.h> +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" + +namespace tracing { + +TracingApp::TracingApp() : collector_binding_(this), tracing_active_(false) { +} + +TracingApp::~TracingApp() { +} + +bool TracingApp::AcceptConnection(shell::Connection* connection) { + connection->AddInterface<TraceCollector>(this); + connection->AddInterface<StartupPerformanceDataCollector>(this); + + // If someone connects to us they may want to use the TraceCollector + // interface and/or they may want to expose themselves to be traced. Attempt + // to connect to the TraceProvider interface to see if the application + // connecting to us wants to be traced. They can refuse the connection or + // close the pipe if not. + TraceProviderPtr provider_ptr; + connection->GetInterface(&provider_ptr); + if (tracing_active_) { + TraceRecorderPtr recorder_ptr; + recorder_impls_.push_back( + new TraceRecorderImpl(GetProxy(&recorder_ptr), sink_.get())); + provider_ptr->StartTracing(tracing_categories_, std::move(recorder_ptr)); + } + provider_ptrs_.AddPtr(std::move(provider_ptr)); + return true; +} + +bool TracingApp::ShellConnectionLost() { + // TODO(beng): This is only required because TracingApp isn't run by + // ApplicationRunner - instead it's launched automatically by the standalone + // shell. It shouldn't be. + base::MessageLoop::current()->QuitWhenIdle(); + return false; +} + +void TracingApp::Create(shell::Connection* connection, + mojo::InterfaceRequest<TraceCollector> request) { + collector_binding_.Bind(std::move(request)); +} + +void TracingApp::Create( + shell::Connection* connection, + mojo::InterfaceRequest<StartupPerformanceDataCollector> request) { + startup_performance_data_collector_bindings_.AddBinding(this, + std::move(request)); +} + +void TracingApp::Start(mojo::ScopedDataPipeProducerHandle stream, + const mojo::String& categories) { + tracing_categories_ = categories; + sink_.reset(new TraceDataSink(std::move(stream))); + provider_ptrs_.ForAllPtrs([categories, this](TraceProvider* controller) { + TraceRecorderPtr ptr; + recorder_impls_.push_back( + new TraceRecorderImpl(GetProxy(&ptr), sink_.get())); + controller->StartTracing(categories, std::move(ptr)); + }); + tracing_active_ = true; +} + +void TracingApp::StopAndFlush() { + // Remove any collectors that closed their message pipes before we called + // StopTracing(). + for (int i = static_cast<int>(recorder_impls_.size()) - 1; i >= 0; --i) { + if (!recorder_impls_[i]->TraceRecorderHandle().is_valid()) { + recorder_impls_.erase(recorder_impls_.begin() + i); + } + } + + tracing_active_ = false; + provider_ptrs_.ForAllPtrs( + [](TraceProvider* controller) { controller->StopTracing(); }); + + // Sending the StopTracing message to registered controllers will request that + // they send trace data back via the collector interface and, when they are + // done, close the collector pipe. We don't know how long they will take. We + // want to read all data that any collector might send until all collectors or + // closed or an (arbitrary) deadline has passed. Since the bindings don't + // support this directly we do our own MojoWaitMany over the handles and read + // individual messages until all are closed or our absolute deadline has + // elapsed. + static const MojoDeadline kTimeToWaitMicros = 1000 * 1000; + MojoTimeTicks end = MojoGetTimeTicksNow() + kTimeToWaitMicros; + + while (!recorder_impls_.empty()) { + MojoTimeTicks now = MojoGetTimeTicksNow(); + if (now >= end) // Timed out? + break; + + MojoDeadline mojo_deadline = end - now; + std::vector<mojo::Handle> handles; + std::vector<MojoHandleSignals> signals; + for (const auto& it : recorder_impls_) { + handles.push_back(it->TraceRecorderHandle()); + signals.push_back(MOJO_HANDLE_SIGNAL_READABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED); + } + std::vector<MojoHandleSignalsState> signals_states(signals.size()); + const mojo::WaitManyResult wait_many_result = + mojo::WaitMany(handles, signals, mojo_deadline, &signals_states); + if (wait_many_result.result == MOJO_RESULT_DEADLINE_EXCEEDED) { + // Timed out waiting, nothing more to read. + LOG(WARNING) << "Timed out waiting for trace flush"; + break; + } + if (wait_many_result.IsIndexValid()) { + // Iterate backwards so we can remove closed pipes from |recorder_impls_| + // without invalidating subsequent offsets. + for (size_t i = signals_states.size(); i != 0; --i) { + size_t index = i - 1; + MojoHandleSignals satisfied = signals_states[index].satisfied_signals; + // To avoid dropping data, don't close unless there's no + // readable signal. + if (satisfied & MOJO_HANDLE_SIGNAL_READABLE) + recorder_impls_[index]->TryRead(); + else if (satisfied & MOJO_HANDLE_SIGNAL_PEER_CLOSED) + recorder_impls_.erase(recorder_impls_.begin() + index); + } + } + } + AllDataCollected(); +} + +void TracingApp::SetShellProcessCreationTime(int64_t time) { + if (startup_performance_times_.shell_process_creation_time == 0) + startup_performance_times_.shell_process_creation_time = time; +} + +void TracingApp::SetShellMainEntryPointTime(int64_t time) { + if (startup_performance_times_.shell_main_entry_point_time == 0) + startup_performance_times_.shell_main_entry_point_time = time; +} + +void TracingApp::SetBrowserMessageLoopStartTicks(int64_t ticks) { + if (startup_performance_times_.browser_message_loop_start_ticks == 0) + startup_performance_times_.browser_message_loop_start_ticks = ticks; +} + +void TracingApp::SetBrowserWindowDisplayTicks(int64_t ticks) { + if (startup_performance_times_.browser_window_display_ticks == 0) + startup_performance_times_.browser_window_display_ticks = ticks; +} + +void TracingApp::SetBrowserOpenTabsTimeDelta(int64_t delta) { + if (startup_performance_times_.browser_open_tabs_time_delta == 0) + startup_performance_times_.browser_open_tabs_time_delta = delta; +} + +void TracingApp::SetFirstWebContentsMainFrameLoadTicks(int64_t ticks) { + if (startup_performance_times_.first_web_contents_main_frame_load_ticks == 0) + startup_performance_times_.first_web_contents_main_frame_load_ticks = ticks; +} + +void TracingApp::SetFirstVisuallyNonEmptyLayoutTicks(int64_t ticks) { + if (startup_performance_times_.first_visually_non_empty_layout_ticks == 0) + startup_performance_times_.first_visually_non_empty_layout_ticks = ticks; +} + +void TracingApp::GetStartupPerformanceTimes( + const GetStartupPerformanceTimesCallback& callback) { + callback.Run(startup_performance_times_.Clone()); +} + +void TracingApp::AllDataCollected() { + recorder_impls_.clear(); + sink_.reset(); +} + +} // namespace tracing diff --git a/chromium/services/tracing/tracing_app.h b/chromium/services/tracing/tracing_app.h new file mode 100644 index 00000000000..6ba6bfd49ad --- /dev/null +++ b/chromium/services/tracing/tracing_app.h @@ -0,0 +1,82 @@ +// 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. + +#ifndef SERVICES_TRACING_TRACING_APP_H_ +#define SERVICES_TRACING_TRACING_APP_H_ + +#include <stdint.h> + +#include <memory> + +#include "base/macros.h" +#include "base/memory/scoped_vector.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/shell/public/cpp/interface_factory.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/tracing/public/interfaces/tracing.mojom.h" +#include "services/tracing/trace_data_sink.h" +#include "services/tracing/trace_recorder_impl.h" + +namespace tracing { + +class TracingApp + : public shell::ShellClient, + public shell::InterfaceFactory<TraceCollector>, + public TraceCollector, + public shell::InterfaceFactory<StartupPerformanceDataCollector>, + public StartupPerformanceDataCollector { + public: + TracingApp(); + ~TracingApp() override; + + private: + // shell::ShellClient implementation. + bool AcceptConnection(shell::Connection* connection) override; + bool ShellConnectionLost() override; + + // shell::InterfaceFactory<TraceCollector> implementation. + void Create(shell::Connection* connection, + mojo::InterfaceRequest<TraceCollector> request) override; + + // shell::InterfaceFactory<StartupPerformanceDataCollector> implementation. + void Create( + shell::Connection* connection, + mojo::InterfaceRequest<StartupPerformanceDataCollector> request) override; + + // tracing::TraceCollector implementation. + void Start(mojo::ScopedDataPipeProducerHandle stream, + const mojo::String& categories) override; + void StopAndFlush() override; + + // StartupPerformanceDataCollector implementation. + void SetShellProcessCreationTime(int64_t time) override; + void SetShellMainEntryPointTime(int64_t time) override; + void SetBrowserMessageLoopStartTicks(int64_t ticks) override; + void SetBrowserWindowDisplayTicks(int64_t ticks) override; + void SetBrowserOpenTabsTimeDelta(int64_t delta) override; + void SetFirstWebContentsMainFrameLoadTicks(int64_t ticks) override; + void SetFirstVisuallyNonEmptyLayoutTicks(int64_t ticks) override; + void GetStartupPerformanceTimes( + const GetStartupPerformanceTimesCallback& callback) override; + + void AllDataCollected(); + + std::unique_ptr<TraceDataSink> sink_; + ScopedVector<TraceRecorderImpl> recorder_impls_; + mojo::InterfacePtrSet<TraceProvider> provider_ptrs_; + mojo::Binding<TraceCollector> collector_binding_; + mojo::BindingSet<StartupPerformanceDataCollector> + startup_performance_data_collector_bindings_; + StartupPerformanceTimes startup_performance_times_; + bool tracing_active_; + mojo::String tracing_categories_; + + DISALLOW_COPY_AND_ASSIGN(TracingApp); +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_TRACING_APP_H_ diff --git a/chromium/services/user/BUILD.gn b/chromium/services/user/BUILD.gn new file mode 100644 index 00000000000..d30ff4660ef --- /dev/null +++ b/chromium/services/user/BUILD.gn @@ -0,0 +1,42 @@ +# 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. + +import("//mojo/public/mojo_application.gni") +import("//mojo/public/mojo_application_manifest.gni") + +source_set("lib") { + sources = [ + "user_id_map.cc", + "user_id_map.h", + "user_service.cc", + "user_service.h", + "user_shell_client.cc", + "user_shell_client.h", + ] + + deps = [ + "//base", + "//components/filesystem:lib", + "//components/filesystem/public/interfaces", + "//components/leveldb:lib", + "//components/leveldb/public/interfaces", + "//mojo/common", + "//mojo/common:common_base", + "//mojo/platform_handle", + "//services/shell/public/cpp", + "//services/shell/public/interfaces", + "//services/tracing/public/cpp", + "//services/user/public/interfaces", + "//url", + ] + + data_deps = [ + ":manifest", + ] +} + +mojo_application_manifest("manifest") { + application_name = "user" + source = "manifest.json" +} diff --git a/chromium/services/user/DEPS b/chromium/services/user/DEPS new file mode 100644 index 00000000000..5f2952d7211 --- /dev/null +++ b/chromium/services/user/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+components/filesystem", + "+components/leveldb", + "+services/tracing/public/cpp", +] diff --git a/chromium/services/user/OWNERS b/chromium/services/user/OWNERS new file mode 100644 index 00000000000..4733a4f06bf --- /dev/null +++ b/chromium/services/user/OWNERS @@ -0,0 +1 @@ +erg@chromium.org diff --git a/chromium/services/user/manifest.json b/chromium/services/user/manifest.json new file mode 100644 index 00000000000..246d3922147 --- /dev/null +++ b/chromium/services/user/manifest.json @@ -0,0 +1,11 @@ +{ + "manifest_version": 1, + "name": "mojo:user", + "process-group": "browser", + "display_name": "User Service", + "capabilities": { + "required": { + "*": { "classes": [ "app" ] } + } + } +} diff --git a/chromium/services/user/public/cpp/BUILD.gn b/chromium/services/user/public/cpp/BUILD.gn new file mode 100644 index 00000000000..f341a18b19a --- /dev/null +++ b/chromium/services/user/public/cpp/BUILD.gn @@ -0,0 +1,10 @@ +# 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. + +source_set("cpp") { + sources = [ + "constants.cc", + "constants.h", + ] +} diff --git a/chromium/services/user/public/cpp/constants.cc b/chromium/services/user/public/cpp/constants.cc new file mode 100644 index 00000000000..963fb12375e --- /dev/null +++ b/chromium/services/user/public/cpp/constants.cc @@ -0,0 +1,11 @@ +// 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 "services/user/public/cpp/constants.h" + +namespace user_service { + +const char kUserServiceName[] = "mojo:user"; + +} // namespace user_service diff --git a/chromium/services/user/public/cpp/constants.h b/chromium/services/user/public/cpp/constants.h new file mode 100644 index 00000000000..64a18c2b5df --- /dev/null +++ b/chromium/services/user/public/cpp/constants.h @@ -0,0 +1,14 @@ +// 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. + +#ifndef SERVICES_USER_PUBLIC_CPP_CONSTANTS_H_ +#define SERVICES_USER_PUBLIC_CPP_CONSTANTS_H_ + +namespace user_service { + +extern const char kUserServiceName[]; + +} // namespace user_service + +#endif // SERVICES_USER_PUBLIC_CPP_CONSTANTS_H_ diff --git a/chromium/services/user/public/interfaces/BUILD.gn b/chromium/services/user/public/interfaces/BUILD.gn new file mode 100644 index 00000000000..0f2415baa1d --- /dev/null +++ b/chromium/services/user/public/interfaces/BUILD.gn @@ -0,0 +1,15 @@ +# 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { + sources = [ + "user_service.mojom", + ] + + deps = [ + "//components/filesystem/public/interfaces", + ] +} diff --git a/chromium/services/user/public/interfaces/OWNERS b/chromium/services/user/public/interfaces/OWNERS new file mode 100644 index 00000000000..9e621796752 --- /dev/null +++ b/chromium/services/user/public/interfaces/OWNERS @@ -0,0 +1,13 @@ +# Changes to Mojo interfaces require a security review to avoid +# introducing new sandbox escapes. +per-file *.mojom=set noparent +per-file *.mojom=dcheng@chromium.org +per-file *.mojom=inferno@chromium.org +per-file *.mojom=jln@chromium.org +per-file *.mojom=jschuh@chromium.org +per-file *.mojom=kenrb@chromium.org +per-file *.mojom=mkwst@chromium.org +per-file *.mojom=nasko@chromium.org +per-file *.mojom=palmer@chromium.org +per-file *.mojom=tsepez@chromium.org +per-file *.mojom=wfh@chromium.org diff --git a/chromium/services/user/public/interfaces/user_service.mojom b/chromium/services/user/public/interfaces/user_service.mojom new file mode 100644 index 00000000000..f1cf6337d07 --- /dev/null +++ b/chromium/services/user/public/interfaces/user_service.mojom @@ -0,0 +1,23 @@ +// 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. + +module user_service.mojom; + +import "components/filesystem/public/interfaces/directory.mojom"; +import "components/filesystem/public/interfaces/types.mojom"; + +// An encapsulation around the per-profile storage. +// +// TODO(erg): An instance of UserService should be strongly bound to a specific +// user id; eventually during startup of the User Service process, we sandbox +// the process so the only directory it has access to is the user's directory. +interface UserService { + // Returns the user profile directory. + GetDirectory(filesystem.mojom.Directory& dir) => (); + + // Returns a subdirectory under the profile dir. Returns a filesystem error + // when we fail to create the subdirectory. + GetSubDirectory(string dir_path, filesystem.mojom.Directory& dir) + => (filesystem.mojom.FileError err); +}; diff --git a/chromium/services/user/user.gyp b/chromium/services/user/user.gyp new file mode 100644 index 00000000000..328fc6cf1db --- /dev/null +++ b/chromium/services/user/user.gyp @@ -0,0 +1,95 @@ +# 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. + +{ + 'variables': { + # This turns on e.g. the filename-based detection of which + # platforms to include source files on (e.g. files ending in + # _mac.h or _mac.cc are only compiled on MacOSX). + 'chromium_code': 1, + }, + 'targets': [ + { + # GN version: //services/user:lib + 'target_name': 'user_service_lib', + 'type': 'static_library', + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'user_id_map.cc', + 'user_id_map.h', + 'user_service.cc', + 'user_service.h', + 'user_shell_client.cc', + 'user_shell_client.h', + ], + 'dependencies': [ + 'user_app_manifest', + 'user_service_bindings', + '../../base/base.gyp:base', + '../../components/filesystem/filesystem.gyp:filesystem_lib', + '../../components/leveldb/leveldb.gyp:leveldb_lib', + '../../services/shell/shell_public.gyp:shell_public', + '../../services/tracing/tracing.gyp:tracing_lib', + '../../mojo/mojo_edk.gyp:mojo_system_impl', + '../../mojo/mojo_public.gyp:mojo_cpp_bindings', + '../../mojo/mojo_platform_handle.gyp:platform_handle', + '../../url/url.gyp:url_lib', + ], + 'export_dependent_settings': [ + 'user_service_bindings', + ], + }, + { + # GN version: //services/user/public/interfaces + 'target_name': 'user_service_bindings', + 'type': 'static_library', + 'dependencies': [ + 'user_service_bindings_mojom', + ], + }, + { + 'target_name': 'user_service_bindings_mojom', + 'type': 'none', + 'variables': { + 'mojom_files': [ + 'public/interfaces/user_service.mojom', + ], + }, + 'dependencies': [ + '../../components/filesystem/filesystem.gyp:filesystem_bindings_mojom', + '../../components/leveldb/leveldb.gyp:leveldb_bindings_mojom', + ], + 'includes': [ + '../../mojo/mojom_bindings_generator_explicit.gypi', + ], + }, + { + 'target_name': 'user_service_public_lib', + 'type': 'static_library', + 'sources': [ + 'public/cpp/constants.cc', + 'public/cpp/constants.h', + ], + 'include_dirs': [ + '../..', + ], + }, + { + # GN version: //services/user:manifest + 'target_name': 'user_app_manifest', + 'type': 'none', + 'variables': { + 'application_type': 'mojo', + 'application_name': 'user', + 'source_manifest': '<(DEPTH)/services/user/manifest.json', + }, + 'includes': [ + '../../mojo/public/mojo_application_manifest.gypi', + ], + 'hard_dependency': 1, + }, + ], +} diff --git a/chromium/services/user/user_id_map.cc b/chromium/services/user/user_id_map.cc new file mode 100644 index 00000000000..0195f96b432 --- /dev/null +++ b/chromium/services/user/user_id_map.cc @@ -0,0 +1,29 @@ +// 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 "services/user/user_id_map.h" + +#include <map> + +#include "base/lazy_instance.h" + +namespace user_service { + +namespace { +base::LazyInstance<std::map<std::string, base::FilePath>> + g_user_id_to_data_dir = LAZY_INSTANCE_INITIALIZER; +} // namespace + +void AssociateMojoUserIDWithUserDir(const std::string& user_id, + const base::FilePath& user_dir) { + g_user_id_to_data_dir.Get()[user_id] = user_dir; +} + +base::FilePath GetUserDirForUserID(const std::string& user_id) { + auto it = g_user_id_to_data_dir.Get().find(user_id); + DCHECK(it != g_user_id_to_data_dir.Get().end()); + return it->second; +} + +} // namespace user_service diff --git a/chromium/services/user/user_id_map.h b/chromium/services/user/user_id_map.h new file mode 100644 index 00000000000..217a15c6189 --- /dev/null +++ b/chromium/services/user/user_id_map.h @@ -0,0 +1,31 @@ +// 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. + +#ifndef SERVICES_USER_USER_ID_MAP_H_ +#define SERVICES_USER_USER_ID_MAP_H_ + +#include <string> +#include "base/files/file_path.h" + +namespace user_service { + +// Currently, UserApp is run from within the chrome process. This means that +// the ApplicationLoader is registered during MojoShellContext startup, even +// though the application itself is not started. As soon as a BrowserContext is +// created, the BrowserContext will choose a |user_id| for itself and call us +// to register the mapping from |user_id| to |user_dir|. +// +// This data is then accessed when we get our Initialize() call. +// +// TODO(erg): This is a temporary hack until we redo how we initialize mojo +// applications inside of chrome in general; this system won't work once +// UserApp gets put in its own sandboxed process. +void AssociateMojoUserIDWithUserDir(const std::string& user_id, + const base::FilePath& user_dir); + +base::FilePath GetUserDirForUserID(const std::string& user_id); + +} // namespace user_service + +#endif // SERVICES_USER_USER_ID_MAP_H_ diff --git a/chromium/services/user/user_service.cc b/chromium/services/user/user_service.cc new file mode 100644 index 00000000000..59d91107fcf --- /dev/null +++ b/chromium/services/user/user_service.cc @@ -0,0 +1,57 @@ +// 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 "services/user/user_service.h" + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/strings/utf_string_conversions.h" +#include "components/filesystem/directory_impl.h" +#include "components/filesystem/lock_table.h" +#include "components/filesystem/public/interfaces/types.mojom.h" +#include "services/shell/public/cpp/connection.h" + +namespace user_service { + +UserService::UserService(const base::FilePath& base_user_dir, + const scoped_refptr<filesystem::LockTable>& lock_table) + : lock_table_(lock_table), path_(base_user_dir) { + base::CreateDirectory(path_); +} + +UserService::~UserService() {} + +void UserService::GetDirectory(filesystem::mojom::DirectoryRequest request, + const GetDirectoryCallback& callback) { + new filesystem::DirectoryImpl(std::move(request), path_, + scoped_refptr<filesystem::SharedTempDir>(), + lock_table_); + callback.Run(); +} + +void UserService::GetSubDirectory(const mojo::String& sub_directory_path, + filesystem::mojom::DirectoryRequest request, + const GetSubDirectoryCallback& callback) { + // Ensure that we've made |subdirectory| recursively under our user dir. + base::FilePath subdir = path_.Append( +#if defined(OS_WIN) + base::UTF8ToWide(sub_directory_path.To<std::string>())); +#else + sub_directory_path.To<std::string>()); +#endif + base::File::Error error; + if (!base::CreateDirectoryAndGetError(subdir, &error)) { + callback.Run(static_cast<filesystem::mojom::FileError>(error)); + return; + } + + new filesystem::DirectoryImpl(std::move(request), subdir, + scoped_refptr<filesystem::SharedTempDir>(), + lock_table_); + callback.Run(filesystem::mojom::FileError::OK); +} + +} // namespace user_service diff --git a/chromium/services/user/user_service.h b/chromium/services/user/user_service.h new file mode 100644 index 00000000000..2aeee76f5be --- /dev/null +++ b/chromium/services/user/user_service.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef SERVICES_USER_USER_SERVICE_IMPL_H_ +#define SERVICES_USER_USER_SERVICE_IMPL_H_ + +#include "base/files/file_path.h" +#include "components/filesystem/public/interfaces/directory.mojom.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/shell/public/cpp/connection.h" +#include "services/user/public/interfaces/user_service.mojom.h" + +namespace filesystem { +class LockTable; +} + +namespace user_service { + +// A service which serves directories to callers. +class UserService : public mojom::UserService { + public: + UserService(const base::FilePath& base_user_dir, + const scoped_refptr<filesystem::LockTable>& lock_table); + ~UserService() override; + + // Overridden from mojom::UserService: + void GetDirectory(filesystem::mojom::DirectoryRequest request, + const GetDirectoryCallback& callback) override; + void GetSubDirectory(const mojo::String& sub_directory_path, + filesystem::mojom::DirectoryRequest request, + const GetSubDirectoryCallback& callback) override; + + private: + scoped_refptr<filesystem::LockTable> lock_table_; + base::FilePath path_; + + DISALLOW_COPY_AND_ASSIGN(UserService); +}; + +} // namespace user_service + +#endif // SERVICES_USER_USER_SERVICE_IMPL_H_ diff --git a/chromium/services/user/user_shell_client.cc b/chromium/services/user/user_shell_client.cc new file mode 100644 index 00000000000..bd20927959b --- /dev/null +++ b/chromium/services/user/user_shell_client.cc @@ -0,0 +1,126 @@ +// 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 "services/user/user_shell_client.h" + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" +#include "components/filesystem/lock_table.h" +#include "components/leveldb/leveldb_service_impl.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "services/shell/public/cpp/connection.h" +#include "services/user/user_id_map.h" +#include "services/user/user_service.h" + +namespace user_service { + +class UserShellClient::UserServiceObjects + : public base::SupportsWeakPtr<UserServiceObjects> { + public: + // Created on the main thread. + UserServiceObjects(base::FilePath user_dir) : user_dir_(user_dir) {} + + // Destroyed on the |user_service_runner_|. + ~UserServiceObjects() {} + + // Called on the |user_service_runner_|. + void OnUserServiceRequest(shell::Connection* connection, + mojom::UserServiceRequest request) { + if (!lock_table_) + lock_table_ = new filesystem::LockTable; + user_service_bindings_.AddBinding(new UserService(user_dir_, lock_table_), + std::move(request)); + } + + private: + mojo::BindingSet<mojom::UserService> user_service_bindings_; + scoped_refptr<filesystem::LockTable> lock_table_; + base::FilePath user_dir_; + + DISALLOW_COPY_AND_ASSIGN(UserServiceObjects); +}; + +class UserShellClient::LevelDBServiceObjects + : public base::SupportsWeakPtr<LevelDBServiceObjects> { + public: + // Created on the main thread. + LevelDBServiceObjects(scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : task_runner_(std::move(task_runner)) {} + + // Destroyed on the |leveldb_service_runner_|. + ~LevelDBServiceObjects() {} + + // Called on the |leveldb_service_runner_|. + void OnLevelDBServiceRequest(shell::Connection* connection, + leveldb::mojom::LevelDBServiceRequest request) { + if (!leveldb_service_) + leveldb_service_.reset(new leveldb::LevelDBServiceImpl(task_runner_)); + leveldb_bindings_.AddBinding(leveldb_service_.get(), std::move(request)); + } + + private: + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + + // Variables that are only accessible on the |leveldb_service_runner_| thread. + std::unique_ptr<leveldb::mojom::LevelDBService> leveldb_service_; + mojo::BindingSet<leveldb::mojom::LevelDBService> leveldb_bindings_; + + DISALLOW_COPY_AND_ASSIGN(LevelDBServiceObjects); +}; + +std::unique_ptr<shell::ShellClient> CreateUserShellClient( + scoped_refptr<base::SingleThreadTaskRunner> user_service_runner, + scoped_refptr<base::SingleThreadTaskRunner> leveldb_service_runner, + const base::Closure& quit_closure) { + return base::WrapUnique(new UserShellClient( + std::move(user_service_runner), std::move(leveldb_service_runner))); +} + +UserShellClient::UserShellClient( + scoped_refptr<base::SingleThreadTaskRunner> user_service_runner, + scoped_refptr<base::SingleThreadTaskRunner> leveldb_service_runner) + : user_service_runner_(std::move(user_service_runner)), + leveldb_service_runner_(std::move(leveldb_service_runner)) {} + +UserShellClient::~UserShellClient() { + user_service_runner_->DeleteSoon(FROM_HERE, user_objects_.release()); + leveldb_service_runner_->DeleteSoon(FROM_HERE, leveldb_objects_.release()); +} + +void UserShellClient::Initialize(shell::Connector* connector, + const shell::Identity& identity, + uint32_t id) { + user_objects_.reset(new UserShellClient::UserServiceObjects( + GetUserDirForUserID(identity.user_id()))); + leveldb_objects_.reset( + new UserShellClient::LevelDBServiceObjects(leveldb_service_runner_)); +} + +bool UserShellClient::AcceptConnection(shell::Connection* connection) { + connection->AddInterface<leveldb::mojom::LevelDBService>(this); + connection->AddInterface<mojom::UserService>(this); + return true; +} + +void UserShellClient::Create(shell::Connection* connection, + mojom::UserServiceRequest request) { + user_service_runner_->PostTask( + FROM_HERE, + base::Bind(&UserShellClient::UserServiceObjects::OnUserServiceRequest, + user_objects_->AsWeakPtr(), connection, + base::Passed(&request))); +} + +void UserShellClient::Create(shell::Connection* connection, + leveldb::mojom::LevelDBServiceRequest request) { + leveldb_service_runner_->PostTask( + FROM_HERE, + base::Bind( + &UserShellClient::LevelDBServiceObjects::OnLevelDBServiceRequest, + leveldb_objects_->AsWeakPtr(), connection, + base::Passed(&request))); +} + +} // namespace user_service diff --git a/chromium/services/user/user_shell_client.h b/chromium/services/user/user_shell_client.h new file mode 100644 index 00000000000..205e4168e98 --- /dev/null +++ b/chromium/services/user/user_shell_client.h @@ -0,0 +1,68 @@ +// 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. + +#ifndef SERVICES_USER_USER_SHELL_CLIENT_H_ +#define SERVICES_USER_USER_SHELL_CLIENT_H_ + +#include "base/memory/ref_counted.h" +#include "components/filesystem/lock_table.h" +#include "components/leveldb/public/interfaces/leveldb.mojom.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/shell/public/cpp/interface_factory.h" +#include "services/shell/public/cpp/shell_client.h" +#include "services/user/public/interfaces/user_service.mojom.h" + +namespace user_service { + +std::unique_ptr<shell::ShellClient> CreateUserShellClient( + scoped_refptr<base::SingleThreadTaskRunner> user_service_runner, + scoped_refptr<base::SingleThreadTaskRunner> leveldb_service_runner, + const base::Closure& quit_closure); + +class UserShellClient + : public shell::ShellClient, + public shell::InterfaceFactory<mojom::UserService>, + public shell::InterfaceFactory<leveldb::mojom::LevelDBService> { + public: + UserShellClient( + scoped_refptr<base::SingleThreadTaskRunner> user_service_runner, + scoped_refptr<base::SingleThreadTaskRunner> leveldb_service_runner); + ~UserShellClient() override; + + private: + // |ShellClient| override: + void Initialize(shell::Connector* connector, + const shell::Identity& identity, + uint32_t id) override; + bool AcceptConnection(shell::Connection* connection) override; + + // |InterfaceFactory<mojom::UserService>| implementation: + void Create(shell::Connection* connection, + mojom::UserServiceRequest request) override; + + // |InterfaceFactory<LevelDBService>| implementation: + void Create(shell::Connection* connection, + leveldb::mojom::LevelDBServiceRequest request) override; + + void OnLevelDBServiceRequest(shell::Connection* connection, + leveldb::mojom::LevelDBServiceRequest request); + void OnLevelDBServiceError(); + + scoped_refptr<base::SingleThreadTaskRunner> user_service_runner_; + scoped_refptr<base::SingleThreadTaskRunner> leveldb_service_runner_; + + // We create these two objects so we can delete them on the correct task + // runners. + class UserServiceObjects; + std::unique_ptr<UserServiceObjects> user_objects_; + + class LevelDBServiceObjects; + std::unique_ptr<LevelDBServiceObjects> leveldb_objects_; + + DISALLOW_COPY_AND_ASSIGN(UserShellClient); +}; + +} // namespace user_service + +#endif // SERVICES_USER_USER_SHELL_CLIENT_H_ |