summaryrefslogtreecommitdiffstats
path: root/mojo
diff options
context:
space:
mode:
authorben <ben@chromium.org>2016-03-09 17:21:58 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-10 01:23:41 +0000
commitd1faec7945458e2500203bd9a7d37cd462646447 (patch)
treede8557ee265788379e526345d4232aef1828100b /mojo
parentcbe35bac4f296de200b2821d8406d921b83956ba (diff)
downloadchromium_src-d1faec7945458e2500203bd9a7d37cd462646447.zip
chromium_src-d1faec7945458e2500203bd9a7d37cd462646447.tar.gz
chromium_src-d1faec7945458e2500203bd9a7d37cd462646447.tar.bz2
Morph CapabilityFilter into caps::Capabilities, which supports capability classes (yet unimplemented).
R=sky@chromium.org BUG= Review URL: https://codereview.chromium.org/1775113003 Cr-Commit-Position: refs/heads/master@{#380285}
Diffstat (limited to 'mojo')
-rw-r--r--mojo/mojo_base.gyp3
-rw-r--r--mojo/services/catalog/builder.cc140
-rw-r--r--mojo/services/catalog/builder.h2
-rw-r--r--mojo/services/catalog/builder_unittest.cc55
-rw-r--r--mojo/services/catalog/catalog.cc21
-rw-r--r--mojo/services/catalog/data/serialization18
-rw-r--r--mojo/services/catalog/entry.cc6
-rw-r--r--mojo/services/catalog/entry.h14
-rw-r--r--mojo/services/catalog/store.cc10
-rw-r--r--mojo/services/catalog/store.h13
-rw-r--r--mojo/shell/public/cpp/BUILD.gn2
-rw-r--r--mojo/shell/public/cpp/capabilities.h65
-rw-r--r--mojo/shell/public/cpp/lib/application_test_base.cc2
-rw-r--r--mojo/shell/public/cpp/lib/capabilities.cc70
-rw-r--r--mojo/shell/public/cpp/lib/shell_connection.cc5
-rw-r--r--mojo/shell/public/cpp/shell_connection.h2
-rw-r--r--mojo/shell/public/interfaces/BUILD.gn1
-rw-r--r--mojo/shell/public/interfaces/capabilities.mojom47
-rw-r--r--mojo/shell/public/interfaces/shell.mojom1
-rw-r--r--mojo/shell/public/interfaces/shell_client.mojom3
-rw-r--r--mojo/shell/public/interfaces/shell_resolver.mojom16
-rw-r--r--mojo/shell/shell.cc87
-rw-r--r--mojo/shell/shell.h30
-rw-r--r--mojo/shell/tests/connect/connect_test_app_manifest.json5
24 files changed, 462 insertions, 156 deletions
diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp
index a3f6ba6..69f970a 100644
--- a/mojo/mojo_base.gyp
+++ b/mojo/mojo_base.gyp
@@ -126,6 +126,7 @@
'mojom_files': [
'services/catalog/public/interfaces/catalog.mojom',
'services/catalog/public/interfaces/resolver.mojom',
+ 'shell/public/interfaces/capabilities.mojom',
'shell/public/interfaces/connector.mojom',
'shell/public/interfaces/interface_provider.mojom',
'shell/public/interfaces/shell.mojom',
@@ -142,6 +143,7 @@
'type': 'static_library',
'sources': [
'shell/public/cpp/application_runner.h',
+ 'shell/public/cpp/capabilities.h',
'shell/public/cpp/connect.h',
'shell/public/cpp/connection.h',
'shell/public/cpp/connector.h',
@@ -153,6 +155,7 @@
'shell/public/cpp/interface_factory_impl.h',
'shell/public/cpp/interface_registry.h',
'shell/public/cpp/lib/application_runner.cc',
+ 'shell/public/cpp/lib/capabilities.cc',
'shell/public/cpp/lib/connection_impl.cc',
'shell/public/cpp/lib/connection_impl.h',
'shell/public/cpp/lib/connector_impl.cc',
diff --git a/mojo/services/catalog/builder.cc b/mojo/services/catalog/builder.cc
index cd011120..9d7d5b7 100644
--- a/mojo/services/catalog/builder.cc
+++ b/mojo/services/catalog/builder.cc
@@ -6,30 +6,106 @@
#include "base/values.h"
#include "mojo/services/catalog/store.h"
+#include "mojo/shell/public/cpp/capabilities.h"
#include "mojo/shell/public/cpp/names.h"
+// TODO(beng): this code should do better error handling instead of CHECKing so
+// much.
+
namespace catalog {
-CapabilityFilter BuildCapabilityFilter(const base::DictionaryValue& value) {
- CapabilityFilter filter;
+mojo::CapabilitySpec BuildCapabilitiesV0(
+ const base::DictionaryValue& value) {
+ mojo::CapabilitySpec capabilities;
base::DictionaryValue::Iterator it(value);
for (; !it.IsAtEnd(); it.Advance()) {
const base::ListValue* values = nullptr;
CHECK(it.value().GetAsList(&values));
- AllowedInterfaces interfaces;
+ mojo::CapabilityRequest spec;
for (auto i = values->begin(); i != values->end(); ++i) {
- std::string iface_name;
+ mojo::Interface interface_name;
const base::Value* v = *i;
- CHECK(v->GetAsString(&iface_name));
- interfaces.insert(iface_name);
+ CHECK(v->GetAsString(&interface_name));
+ spec.interfaces.insert(interface_name);
+ }
+ capabilities.required[it.key()] = spec;
+ }
+ return capabilities;
+}
+
+void 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;
+ CHECK(value_value->GetAsString(&value));
+ string_set->insert(value);
+ }
+}
+
+void ReadStringSetFromValue(const base::Value& value,
+ std::set<std::string>* string_set) {
+ const base::ListValue* list_value = nullptr;
+ CHECK(value.GetAsList(&list_value));
+ ReadStringSet(*list_value, string_set);
+}
+
+void 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))
+ CHECK(dictionary.GetList(key, &list_value));
+ if (list_value)
+ ReadStringSet(*list_value, string_set);
+}
+
+mojo::CapabilitySpec BuildCapabilitiesV1(
+ const base::DictionaryValue& value) {
+ mojo::CapabilitySpec capabilities;
+
+ const base::DictionaryValue* provided_value = nullptr;
+ if (value.HasKey(Store::kCapabilities_ProvidedKey)) {
+ CHECK(value.GetDictionary(Store::kCapabilities_ProvidedKey,
+ &provided_value));
+ }
+ if (provided_value) {
+ mojo::CapabilityRequest provided;
+ base::DictionaryValue::Iterator it(*provided_value);
+ for(; !it.IsAtEnd(); it.Advance()) {
+ mojo::Interfaces interfaces;
+ ReadStringSetFromValue(it.value(), &interfaces);
+ capabilities.provided[it.key()] = interfaces;
+ }
+ }
+
+ const base::DictionaryValue* required_value = nullptr;
+ if (value.HasKey(Store::kCapabilities_RequiredKey)) {
+ CHECK(value.GetDictionary(Store::kCapabilities_RequiredKey,
+ &required_value));
+ }
+ if (required_value) {
+ base::DictionaryValue::Iterator it(*required_value);
+ for (; !it.IsAtEnd(); it.Advance()) {
+ mojo::CapabilityRequest spec;
+ const base::DictionaryValue* entry_value = nullptr;
+ CHECK(it.value().GetAsDictionary(&entry_value));
+ ReadStringSetFromDictionary(
+ *entry_value, Store::kCapabilities_ClassesKey, &spec.classes);
+ ReadStringSetFromDictionary(
+ *entry_value, Store::kCapabilities_InterfacesKey, &spec.interfaces);
+ capabilities.required[it.key()] = spec;
}
- filter[it.key()] = interfaces;
}
- return filter;
+ return capabilities;
}
Entry BuildEntry(const base::DictionaryValue& value) {
Entry entry;
+ int manifest_version = 0;
+ if (value.HasKey(Store::kManifestVersionKey))
+ CHECK(value.GetInteger(Store::kManifestVersionKey, &manifest_version));
std::string name_string;
CHECK(value.GetString(Store::kNameKey, &name_string));
CHECK(mojo::IsValidName(name_string)) << "Invalid Name: " << name_string;
@@ -42,23 +118,47 @@ Entry BuildEntry(const base::DictionaryValue& value) {
CHECK(value.GetString(Store::kDisplayNameKey, &entry.display_name));
const base::DictionaryValue* capabilities = nullptr;
CHECK(value.GetDictionary(Store::kCapabilitiesKey, &capabilities));
- entry.capabilities = BuildCapabilityFilter(*capabilities);
-
+ if (manifest_version == 0)
+ entry.capabilities = BuildCapabilitiesV0(*capabilities);
+ else
+ entry.capabilities = BuildCapabilitiesV1(*capabilities);
return entry;
}
-void SerializeEntry(const Entry& entry, base::DictionaryValue** value) {
- *value = new base::DictionaryValue;
- (*value)->SetString(Store::kNameKey, entry.name);
- (*value)->SetString(Store::kDisplayNameKey, entry.display_name);
- base::DictionaryValue* capabilities = new base::DictionaryValue;
- for (const auto& pair : entry.capabilities) {
+scoped_ptr<base::DictionaryValue> SerializeEntry(const Entry& entry) {
+ scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue);
+ value->SetInteger(Store::kManifestVersionKey, 1);
+ value->SetString(Store::kNameKey, entry.name);
+ value->SetString(Store::kDisplayNameKey, entry.display_name);
+ value->SetString(Store::kQualifierKey, entry.qualifier);
+ scoped_ptr<base::DictionaryValue> spec(new base::DictionaryValue);
+
+ scoped_ptr<base::DictionaryValue> provided(new base::DictionaryValue);
+ for (const auto& i : entry.capabilities.provided) {
+ scoped_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));
+
+ scoped_ptr<base::DictionaryValue> required(new base::DictionaryValue);
+ for (const auto& i : entry.capabilities.required) {
+ scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue);
+ scoped_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));
scoped_ptr<base::ListValue> interfaces(new base::ListValue);
- for (const auto& iface_name : pair.second)
- interfaces->AppendString(iface_name);
- capabilities->Set(pair.first, std::move(interfaces));
+ 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));
}
- (*value)->Set(Store::kCapabilitiesKey, make_scoped_ptr(capabilities));
+ spec->Set(Store::kCapabilities_RequiredKey, std::move(required));
+
+ value->Set(Store::kCapabilitiesKey, std::move(spec));
+ return value;
}
} // namespace catalog
diff --git a/mojo/services/catalog/builder.h b/mojo/services/catalog/builder.h
index 2f3e1aa..71eebf1 100644
--- a/mojo/services/catalog/builder.h
+++ b/mojo/services/catalog/builder.h
@@ -15,7 +15,7 @@ namespace catalog {
Entry BuildEntry(const base::DictionaryValue& value);
-void SerializeEntry(const Entry& entry, base::DictionaryValue** value);
+scoped_ptr<base::DictionaryValue> SerializeEntry(const Entry& entry);
} // namespace catalog
diff --git a/mojo/services/catalog/builder_unittest.cc b/mojo/services/catalog/builder_unittest.cc
index 4290a2f..1705be8 100644
--- a/mojo/services/catalog/builder_unittest.cc
+++ b/mojo/services/catalog/builder_unittest.cc
@@ -9,15 +9,27 @@
#include "base/macros.h"
#include "base/path_service.h"
#include "base/values.h"
+#include "mojo/shell/public/cpp/capabilities.h"
#include "mojo/shell/public/cpp/names.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace catalog {
+
class BuilderTest : public testing::Test {
public:
BuilderTest() {}
~BuilderTest() override {}
protected:
+ scoped_ptr<base::Value> ReadEntry(const std::string& manifest, Entry* entry) {
+ DCHECK(entry);
+ scoped_ptr<base::Value> value = ReadManifest(manifest);
+ base::DictionaryValue* dictionary = nullptr;
+ CHECK(value->GetAsDictionary(&dictionary));
+ *entry = BuildEntry(*dictionary);
+ return value;
+ }
+
scoped_ptr<base::Value> ReadManifest(const std::string& manifest) {
base::FilePath manifest_path;
PathService::Get(base::DIR_SOURCE_ROOT, &manifest_path);
@@ -40,13 +52,9 @@ class BuilderTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(BuilderTest);
};
-namespace catalog {
-
TEST_F(BuilderTest, Simple) {
- scoped_ptr<base::Value> value = ReadManifest("simple");
- base::DictionaryValue* dictionary = nullptr;
- CHECK(value->GetAsDictionary(&dictionary));
- Entry entry = BuildEntry(*dictionary);
+ Entry entry;
+ ReadEntry("simple", &entry);
EXPECT_EQ("mojo:foo", entry.name);
EXPECT_EQ(mojo::GetNamePath(entry.name), entry.qualifier);
@@ -54,10 +62,8 @@ TEST_F(BuilderTest, Simple) {
}
TEST_F(BuilderTest, Instance) {
- scoped_ptr<base::Value> value = ReadManifest("instance");
- base::DictionaryValue* dictionary = nullptr;
- CHECK(value->GetAsDictionary(&dictionary));
- Entry entry = BuildEntry(*dictionary);
+ Entry entry;
+ ReadEntry("instance", &entry);
EXPECT_EQ("mojo:foo", entry.name);
EXPECT_EQ("bar", entry.qualifier);
@@ -65,19 +71,30 @@ TEST_F(BuilderTest, Instance) {
}
TEST_F(BuilderTest, Capabilities) {
- scoped_ptr<base::Value> value = ReadManifest("capabilities");
- base::DictionaryValue* dictionary = nullptr;
- CHECK(value->GetAsDictionary(&dictionary));
- Entry entry = BuildEntry(*dictionary);
+ Entry entry;
+ ReadEntry("capabilities", &entry);
EXPECT_EQ("mojo:foo", entry.name);
EXPECT_EQ("bar", entry.qualifier);
EXPECT_EQ("Foo", entry.display_name);
- CapabilityFilter filter;
- AllowedInterfaces interfaces;
- interfaces.insert("mojo::Bar");
- filter["mojo:bar"] = interfaces;
- EXPECT_EQ(filter, entry.capabilities);
+ mojo::CapabilitySpec spec;
+ mojo::CapabilityRequest request;
+ request.interfaces.insert("mojo::Bar");
+ spec.required["mojo:bar"] = request;
+ EXPECT_EQ(spec, entry.capabilities);
+}
+
+TEST_F(BuilderTest, Serialization) {
+ Entry entry;
+ scoped_ptr<base::Value> value = ReadEntry("serialization", &entry);
+
+ scoped_ptr<base::DictionaryValue> serialized(SerializeEntry(entry));
+
+ // 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.
+ Entry reconstituted = BuildEntry(*serialized.get());
+ EXPECT_EQ(entry, reconstituted);
}
TEST_F(BuilderTest, Malformed) {
diff --git a/mojo/services/catalog/catalog.cc b/mojo/services/catalog/catalog.cc
index 6d5518c..661ec21 100644
--- a/mojo/services/catalog/catalog.cc
+++ b/mojo/services/catalog/catalog.cc
@@ -148,16 +148,10 @@ void Catalog::CompleteResolveMojoName(
mojo::GetNamePath(resolved_name) + extension);
}
- mojo::shell::mojom::CapabilityFilterPtr filter(
- mojo::shell::mojom::CapabilityFilter::New());
- filter->filter = mojo::Map<mojo::String, mojo::Array<mojo::String>>();
- for (const auto& entry : entry_iter->second.capabilities) {
- mojo::Array<mojo::String> interfaces;
- for (auto interface_name : entry.second)
- interfaces.push_back(interface_name);
- filter->filter.insert(entry.first, std::move(interfaces));
- }
- callback.Run(resolved_name, qualifier, std::move(filter),
+ mojo::shell::mojom::CapabilitySpecPtr capabilities_ptr =
+ mojo::shell::mojom::CapabilitySpec::From(entry_iter->second.capabilities);
+
+ callback.Run(resolved_name, qualifier, std::move(capabilities_ptr),
file_url.spec());
}
@@ -203,11 +197,8 @@ void Catalog::DeserializeCatalog() {
void Catalog::SerializeCatalog() {
scoped_ptr<base::ListValue> catalog(new base::ListValue);
- for (const auto& entry : catalog_) {
- base::DictionaryValue* dictionary = nullptr;
- SerializeEntry(entry.second, &dictionary);
- catalog->Append(make_scoped_ptr(dictionary));
- }
+ for (const auto& entry : catalog_)
+ catalog->Append(SerializeEntry(entry.second));
if (store_)
store_->UpdateStore(std::move(catalog));
}
diff --git a/mojo/services/catalog/data/serialization b/mojo/services/catalog/data/serialization
new file mode 100644
index 0000000..4605702
--- /dev/null
+++ b/mojo/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/mojo/services/catalog/entry.cc b/mojo/services/catalog/entry.cc
index 9ed445a..9aec915 100644
--- a/mojo/services/catalog/entry.cc
+++ b/mojo/services/catalog/entry.cc
@@ -10,4 +10,10 @@ Entry::Entry() {}
Entry::Entry(const Entry& other) = default;
Entry::~Entry() {}
+bool Entry::operator==(const Entry& other) const {
+ return other.name == name && other.qualifier == qualifier &&
+ other.display_name == display_name &&
+ other.capabilities == capabilities;
+}
+
} // catalog
diff --git a/mojo/services/catalog/entry.h b/mojo/services/catalog/entry.h
index 4f2f64a..4b7ffa8 100644
--- a/mojo/services/catalog/entry.h
+++ b/mojo/services/catalog/entry.h
@@ -5,17 +5,11 @@
#ifndef MOJO_SERVICES_CATALOG_ENTRY_H_
#define MOJO_SERVICES_CATALOG_ENTRY_H_
-#include <map>
-#include <set>
#include <string>
-namespace catalog {
+#include "mojo/shell/public/cpp/capabilities.h"
-// A set of names of interfaces that may be exposed to an application.
-using AllowedInterfaces = std::set<std::string>;
-// A map of allowed applications to allowed interface sets. See shell.mojom for
-// more details.
-using CapabilityFilter = std::map<std::string, AllowedInterfaces>;
+namespace catalog {
// Static information about an application package known to the Catalog.
struct Entry {
@@ -23,10 +17,12 @@ struct Entry {
Entry(const Entry& other);
~Entry();
+ bool operator==(const Entry& other) const;
+
std::string name;
std::string qualifier;
std::string display_name;
- CapabilityFilter capabilities;
+ mojo::CapabilitySpec capabilities;
};
} // namespace catalog
diff --git a/mojo/services/catalog/store.cc b/mojo/services/catalog/store.cc
index 3bce188..c48aa72 100644
--- a/mojo/services/catalog/store.cc
+++ b/mojo/services/catalog/store.cc
@@ -7,6 +7,8 @@
namespace catalog {
// static
+const char Store::kManifestVersionKey[] = "manifest_version";
+// static
const char Store::kNameKey[] = "name";
// static
const char Store::kQualifierKey[] = "process-group";
@@ -14,5 +16,13 @@ const char Store::kQualifierKey[] = "process-group";
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";
} // namespace catalog
diff --git a/mojo/services/catalog/store.h b/mojo/services/catalog/store.h
index 74309b6..cb763d5 100644
--- a/mojo/services/catalog/store.h
+++ b/mojo/services/catalog/store.h
@@ -14,15 +14,24 @@ namespace catalog {
// 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 that maps from the filter to a list of string
- // interfaces.
+ // 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[];
virtual ~Store() {}
diff --git a/mojo/shell/public/cpp/BUILD.gn b/mojo/shell/public/cpp/BUILD.gn
index b1f0345..53318b1 100644
--- a/mojo/shell/public/cpp/BUILD.gn
+++ b/mojo/shell/public/cpp/BUILD.gn
@@ -21,6 +21,7 @@ source_set("cpp_for_chromium") {
source_set("sources") {
sources = [
"application_runner.h",
+ "capabilities.h",
"connect.h",
"connection.h",
"connector.h",
@@ -31,6 +32,7 @@ source_set("sources") {
"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",
diff --git a/mojo/shell/public/cpp/capabilities.h b/mojo/shell/public/cpp/capabilities.h
new file mode 100644
index 0000000..39bc387
--- /dev/null
+++ b/mojo/shell/public/cpp/capabilities.h
@@ -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.
+
+#ifndef MOJO_SHELL_PUBLIC_CPP_CAPABILITIES_H_
+#define MOJO_SHELL_PUBLIC_CPP_CAPABILITIES_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "mojo/shell/public/interfaces/shell_resolver.mojom.h"
+
+namespace mojo {
+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 mojo/shell/public/interfaces/capabilities.mojom for a
+// description of CapabilityRequest and CapabilitySpec.
+
+struct CapabilityRequest {
+ CapabilityRequest();
+ ~CapabilityRequest();
+ bool operator==(const CapabilityRequest& other) const;
+ Classes classes;
+ Interfaces interfaces;
+};
+
+struct CapabilitySpec {
+ CapabilitySpec();
+ ~CapabilitySpec();
+ bool operator==(const CapabilitySpec& other) const;
+ std::map<Class, Interfaces> provided;
+ std::map<Name, CapabilityRequest> required;
+};
+
+template <>
+struct TypeConverter<shell::mojom::CapabilitySpecPtr, CapabilitySpec> {
+ static shell::mojom::CapabilitySpecPtr Convert(
+ const CapabilitySpec& input);
+};
+template <>
+struct TypeConverter<CapabilitySpec, shell::mojom::CapabilitySpecPtr> {
+ static CapabilitySpec Convert(
+ const shell::mojom::CapabilitySpecPtr& input);
+};
+
+template <>
+struct TypeConverter<shell::mojom::CapabilityRequestPtr,
+ CapabilityRequest> {
+ static shell::mojom::CapabilityRequestPtr Convert(
+ const CapabilityRequest& input);
+};
+template <>
+struct TypeConverter<CapabilityRequest,
+ shell::mojom::CapabilityRequestPtr> {
+ static CapabilityRequest Convert(
+ const shell::mojom::CapabilityRequestPtr& input);
+};
+} // namespace mojo
+
+#endif // MOJO_SHELL_PUBLIC_CPP_CAPABILITIES_H_
diff --git a/mojo/shell/public/cpp/lib/application_test_base.cc b/mojo/shell/public/cpp/lib/application_test_base.cc
index dcd7dc3..c895a24 100644
--- a/mojo/shell/public/cpp/lib/application_test_base.cc
+++ b/mojo/shell/public/cpp/lib/application_test_base.cc
@@ -57,7 +57,7 @@ class ShellGrabber : public shell::mojom::ShellClient {
uint32_t source_id,
shell::mojom::InterfaceProviderRequest local_interfaces,
shell::mojom::InterfaceProviderPtr remote_interfaces,
- Array<String> allowed_interfaces,
+ shell::mojom::CapabilityRequestPtr capability_spec,
const String& name) override {
CHECK(false);
}
diff --git a/mojo/shell/public/cpp/lib/capabilities.cc b/mojo/shell/public/cpp/lib/capabilities.cc
new file mode 100644
index 0000000..cc3fc18
--- /dev/null
+++ b/mojo/shell/public/cpp/lib/capabilities.cc
@@ -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.
+
+#include "mojo/shell/public/cpp/capabilities.h"
+
+namespace mojo {
+
+CapabilityRequest::CapabilityRequest() {}
+CapabilityRequest::~CapabilityRequest() {}
+
+bool CapabilityRequest::operator==(const CapabilityRequest& other) const {
+ return other.classes == classes && other.interfaces == interfaces;
+}
+
+CapabilitySpec::CapabilitySpec() {}
+CapabilitySpec::~CapabilitySpec() {}
+
+bool CapabilitySpec::operator==(const CapabilitySpec& other) const {
+ return other.provided == provided && other.required == required;
+}
+
+// static
+shell::mojom::CapabilitySpecPtr
+TypeConverter<shell::mojom::CapabilitySpecPtr, CapabilitySpec>::Convert(
+ const 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
+CapabilitySpec
+TypeConverter<CapabilitySpec, shell::mojom::CapabilitySpecPtr>::Convert(
+ const shell::mojom::CapabilitySpecPtr& input) {
+ CapabilitySpec spec;
+ spec.provided = input->provided.To<std::map<Class, Interfaces>>();
+ spec.required =
+ input->required.To<std::map<Name, CapabilityRequest>>();
+ return spec;
+}
+
+// static
+shell::mojom::CapabilityRequestPtr
+TypeConverter<shell::mojom::CapabilityRequestPtr,
+ CapabilityRequest>::Convert(
+ const 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
+CapabilityRequest
+TypeConverter<CapabilityRequest,
+ shell::mojom::CapabilityRequestPtr>::Convert(
+ const shell::mojom::CapabilityRequestPtr& input) {
+ 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/mojo/shell/public/cpp/lib/shell_connection.cc b/mojo/shell/public/cpp/lib/shell_connection.cc
index 8ee729d..2e2a7ec 100644
--- a/mojo/shell/public/cpp/lib/shell_connection.cc
+++ b/mojo/shell/public/cpp/lib/shell_connection.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/shell/public/cpp/capabilities.h"
#include "mojo/shell/public/cpp/connector.h"
#include "mojo/shell/public/cpp/lib/connection_impl.h"
#include "mojo/shell/public/cpp/lib/connector_impl.h"
@@ -49,12 +50,12 @@ void ShellConnection::AcceptConnection(
uint32_t source_id,
shell::mojom::InterfaceProviderRequest local_interfaces,
shell::mojom::InterfaceProviderPtr remote_interfaces,
- Array<String> allowed_interfaces,
+ shell::mojom::CapabilityRequestPtr allowed_capabilities,
const String& name) {
scoped_ptr<Connection> registry(new internal::ConnectionImpl(
name, source.To<Identity>(), source_id, std::move(remote_interfaces),
std::move(local_interfaces),
- allowed_interfaces.To<std::set<std::string>>(),
+ allowed_capabilities->interfaces.To<std::set<std::string>>(),
Connection::State::CONNECTED));
if (!client_->AcceptConnection(registry.get()))
return;
diff --git a/mojo/shell/public/cpp/shell_connection.h b/mojo/shell/public/cpp/shell_connection.h
index 7ec245b..e0b1eb1 100644
--- a/mojo/shell/public/cpp/shell_connection.h
+++ b/mojo/shell/public/cpp/shell_connection.h
@@ -66,7 +66,7 @@ class ShellConnection : public shell::mojom::ShellClient {
uint32_t source_id,
shell::mojom::InterfaceProviderRequest remote_interfaces,
shell::mojom::InterfaceProviderPtr local_interfaces,
- Array<String> allowed_interfaces,
+ shell::mojom::CapabilityRequestPtr allowed_capabilities,
const String& name) override;
void OnConnectionError();
diff --git a/mojo/shell/public/interfaces/BUILD.gn b/mojo/shell/public/interfaces/BUILD.gn
index 683e502..0eeac14 100644
--- a/mojo/shell/public/interfaces/BUILD.gn
+++ b/mojo/shell/public/interfaces/BUILD.gn
@@ -7,6 +7,7 @@ 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",
diff --git a/mojo/shell/public/interfaces/capabilities.mojom b/mojo/shell/public/interfaces/capabilities.mojom
new file mode 100644
index 0000000..563b88c
--- /dev/null
+++ b/mojo/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 mojo.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/mojo/shell/public/interfaces/shell.mojom b/mojo/shell/public/interfaces/shell.mojom
index 5e0469f..04dcd1d 100644
--- a/mojo/shell/public/interfaces/shell.mojom
+++ b/mojo/shell/public/interfaces/shell.mojom
@@ -5,7 +5,6 @@
module mojo.shell.mojom;
import "mojo/shell/public/interfaces/connector.mojom";
-import "mojo/shell/public/interfaces/shell_client_factory.mojom";
struct InstanceInfo {
uint32 id;
diff --git a/mojo/shell/public/interfaces/shell_client.mojom b/mojo/shell/public/interfaces/shell_client.mojom
index d6a223e..260bb2c 100644
--- a/mojo/shell/public/interfaces/shell_client.mojom
+++ b/mojo/shell/public/interfaces/shell_client.mojom
@@ -4,6 +4,7 @@
module mojo.shell.mojom;
+import "mojo/shell/public/interfaces/capabilities.mojom";
import "mojo/shell/public/interfaces/connector.mojom";
import "mojo/shell/public/interfaces/interface_provider.mojom";
@@ -69,6 +70,6 @@ interface ShellClient {
uint32 source_id,
InterfaceProvider&? local_interfaces,
InterfaceProvider? remote_interfaces,
- array<string> allowed_interfaces,
+ CapabilityRequest allowed_capabilities,
string resolved_name);
};
diff --git a/mojo/shell/public/interfaces/shell_resolver.mojom b/mojo/shell/public/interfaces/shell_resolver.mojom
index b5e6d29..090bf83 100644
--- a/mojo/shell/public/interfaces/shell_resolver.mojom
+++ b/mojo/shell/public/interfaces/shell_resolver.mojom
@@ -4,19 +4,7 @@
module mojo.shell.mojom;
-// Specifies a whitelist of applications and services an application can connect
-// to. Connections to applications not explicitly specified here as a key are
-// rejected. Connections to services not specified in an application's allowed
-// interfaces value are not made.
-// A "*" value as the only key in an otherwise empty map means the application
-// may connect to any other application.
-// A "*" value as the only string in an otherwise empty array of interface names
-// means the application may connect to any service in that application.
-// An empty interface name array means the application may not connect to any
-// services exposed by the application it is connecting to.
-struct CapabilityFilter {
- map<string, array<string>> filter;
-};
+import "mojo/shell/public/interfaces/capabilities.mojom";
// Implemented exclusively for the Mojo Shell's use in resolving mojo: names
// and reading static manifest information.
@@ -46,6 +34,6 @@ interface ShellResolver {
ResolveMojoName(string mojo_name) =>
(string resolved_mojo_name,
string qualifier,
- CapabilityFilter? filter,
+ CapabilitySpec? capability_spec,
string? mojo_file_url);
};
diff --git a/mojo/shell/shell.cc b/mojo/shell/shell.cc
index 8dac715..1daa93a 100644
--- a/mojo/shell/shell.cc
+++ b/mojo/shell/shell.cc
@@ -38,7 +38,7 @@ const char kCatalogName[] = "mojo:catalog";
void EmptyResolverCallback(const String& resolved_name,
const String& resolved_instance,
- mojom::CapabilityFilterPtr base_filter,
+ mojom::CapabilitySpecPtr capabilities,
const String& file_url) {}
}
@@ -47,28 +47,28 @@ Identity CreateShellIdentity() {
return Identity("mojo:shell", mojom::kRootUserID);
}
-CapabilityFilter GetPermissiveCapabilityFilter() {
- CapabilityFilter filter;
- AllowedInterfaces interfaces;
- interfaces.insert("*");
- filter["*"] = interfaces;
- return filter;
+CapabilitySpec GetPermissiveCapabilities() {
+ CapabilitySpec capabilities;
+ CapabilityRequest spec;
+ spec.interfaces.insert("*");
+ capabilities.required["*"] = spec;
+ return capabilities;
}
-AllowedInterfaces GetAllowedInterfaces(const CapabilityFilter& filter,
- const Identity& identity) {
- // Start by looking for interfaces specific to the supplied identity.
- auto it = filter.find(identity.name());
- if (it != filter.end())
+CapabilityRequest GetCapabilityRequest(const CapabilitySpec& spec,
+ const Identity& identity) {
+ // Start by looking for specs specific to the supplied identity.
+ auto it = spec.required.find(identity.name());
+ if (it != spec.required.end())
return it->second;
// Fall back to looking for a wildcard rule.
- it = filter.find("*");
- if (filter.size() == 1 && it != filter.end())
+ it = spec.required.find("*");
+ if (spec.required.size() == 1 && it != spec.required.end())
return it->second;
// Finally, nothing is allowed.
- return AllowedInterfaces();
+ return CapabilityRequest();
}
// Encapsulates a connection to an instance of an application, tracked by the
@@ -82,12 +82,13 @@ class Shell::Instance : public mojom::Connector,
Instance(mojom::ShellClientPtr shell_client,
mojo::shell::Shell* shell,
const Identity& identity,
- const CapabilityFilter& filter)
+ const CapabilitySpec& capabilities)
: shell_(shell),
id_(GenerateUniqueID()),
identity_(identity),
- filter_(filter),
- allow_any_application_(filter.size() == 1 && filter.count("*") == 1),
+ capabilities_(capabilities),
+ allow_any_application_(capabilities.required.size() == 1 &&
+ capabilities.required.count("*") == 1),
shell_client_(std::move(shell_client)),
pid_receiver_binding_(this),
weak_factory_(this) {
@@ -112,17 +113,17 @@ class Shell::Instance : public mojom::Connector,
params->connect_callback().Run(mojom::ConnectResult::SUCCEEDED,
identity_.user_id(), id_);
uint32_t source_id = mojom::kInvalidInstanceID;
- AllowedInterfaces interfaces;
- interfaces.insert("*");
+ CapabilityRequest spec;
+ spec.interfaces.insert("*");
Instance* source = shell_->GetExistingInstance(params->source());
if (source) {
- interfaces = GetAllowedInterfaces(source->filter_, identity_);
+ spec = GetCapabilityRequest(source->capabilities_, identity_);
source_id = source->id();
}
shell_client_->AcceptConnection(
mojom::Identity::From(params->source()), source_id,
params->TakeRemoteInterfaces(), params->TakeLocalInterfaces(),
- Array<String>::From(interfaces), params->target().name());
+ mojom::CapabilityRequest::From(spec), params->target().name());
}
void StartWithClientProcessConnection(
@@ -185,7 +186,7 @@ class Shell::Instance : public mojom::Connector,
// - a user id other than its own, kInheritUserID or kRootUserID.
// - a non-empty instance name.
// - a non-null client_process_connection.
- if (!ValidateCapabilityFilter(target, callback))
+ if (!ValidateCapabilities(target, callback))
return;
scoped_ptr<ConnectParams> params(new ConnectParams);
@@ -263,13 +264,14 @@ class Shell::Instance : public mojom::Connector,
return true;
}
- bool ValidateCapabilityFilter(const Identity& target,
- const ConnectCallback& callback) {
+ bool ValidateCapabilities(const Identity& target,
+ const ConnectCallback& callback) {
if (allow_any_application_ ||
- filter_.find(target.name()) != filter_.end()) {
+ capabilities_.required.find(target.name()) !=
+ capabilities_.required.end()) {
return true;
}
- LOG(ERROR) << "CapabilityFilter prevented connection from: " <<
+ LOG(ERROR) << "Capabilities prevented connection from: " <<
identity_.name() << " to: " << target.name();
callback.Run(mojom::ConnectResult::ACCESS_DENIED,
mojom::kInheritUserID, mojom::kInvalidInstanceID);
@@ -294,7 +296,7 @@ class Shell::Instance : public mojom::Connector,
// process is launched.
const uint32_t id_;
const Identity identity_;
- const CapabilityFilter filter_;
+ const CapabilitySpec capabilities_;
const bool allow_any_application_;
mojom::ShellClientPtr shell_client_;
Binding<mojom::PIDReceiver> pid_receiver_binding_;
@@ -330,8 +332,7 @@ Shell::Shell(scoped_ptr<NativeRunnerFactory> native_runner_factory,
native_runner_factory_(std::move(native_runner_factory)),
weak_ptr_factory_(this) {
mojom::ShellClientRequest request;
- CreateInstance(CreateShellIdentity(), GetPermissiveCapabilityFilter(),
- &request);
+ CreateInstance(CreateShellIdentity(), GetPermissiveCapabilities(), &request);
shell_connection_.reset(new ShellConnection(this, std::move(request)));
InitCatalog(std::move(catalog_store));
@@ -385,8 +386,8 @@ mojom::ShellClientRequest Shell::InitInstanceForEmbedder(
DCHECK(!GetExistingInstance(target));
mojom::ShellClientRequest request;
- embedder_instance_ = CreateInstance(
- target, GetPermissiveCapabilityFilter(), &request);
+ embedder_instance_ =
+ CreateInstance(target, GetPermissiveCapabilities(), &request);
DCHECK(embedder_instance_);
return request;
@@ -433,7 +434,7 @@ void Shell::InitCatalog(scoped_ptr<catalog::Store> store) {
// TODO(beng): Does the catalog actually have to be run with a permissive
// filter?
Identity identity(name, mojom::kRootUserID);
- CreateInstance(identity, GetPermissiveCapabilityFilter(), &request);
+ CreateInstance(identity, GetPermissiveCapabilities(), &request);
loader_raw->Load(name, std::move(request));
ConnectToInterface(this, CreateShellIdentity(), name, &shell_resolver_);
@@ -495,13 +496,13 @@ bool Shell::ConnectToExistingInstance(scoped_ptr<ConnectParams>* params) {
}
Shell::Instance* Shell::CreateInstance(const Identity& target_id,
- const CapabilityFilter& filter,
+ const CapabilitySpec& capabilities,
mojom::ShellClientRequest* request) {
CHECK(target_id.user_id() != mojom::kInheritUserID);
mojom::ShellClientPtr shell_client;
*request = GetProxy(&shell_client);
Instance* instance =
- new Instance(std::move(shell_client), this, target_id, filter);
+ new Instance(std::move(shell_client), this, target_id, capabilities);
DCHECK(identity_to_instance_.find(target_id) ==
identity_to_instance_.end());
identity_to_instance_[target_id] = instance;
@@ -563,7 +564,7 @@ void Shell::OnShellClientFactoryLost(const Identity& which) {
void Shell::OnGotResolvedName(scoped_ptr<ConnectParams> params,
const String& resolved_name,
const String& resolved_instance,
- mojom::CapabilityFilterPtr base_filter,
+ mojom::CapabilitySpecPtr capabilities_ptr,
const String& file_url) {
std::string instance_name = params->target().instance();
if (instance_name == GetNamePath(params->target().name()) &&
@@ -581,22 +582,22 @@ void Shell::OnGotResolvedName(scoped_ptr<ConnectParams> params,
return;
Identity source = params->source();
- // |base_filter| can be null when there is no manifest, e.g. for URL types
- // not resolvable by the resolver.
- CapabilityFilter filter = GetPermissiveCapabilityFilter();
- if (!base_filter.is_null())
- filter = base_filter->filter.To<CapabilityFilter>();
+ // |capabilities_ptr| can be null when there is no manifest, e.g. for URL
+ // types not resolvable by the resolver.
+ CapabilitySpec capabilities = GetPermissiveCapabilities();
+ if (!capabilities_ptr.is_null())
+ capabilities = capabilities_ptr.To<CapabilitySpec>();
mojom::ClientProcessConnectionPtr client_process_connection =
params->TakeClientProcessConnection();
mojom::ShellClientRequest request;
- Instance* instance = CreateInstance(target, filter, &request);
+ Instance* instance = CreateInstance(target, capabilities, &request);
instance->ConnectToClient(std::move(params));
if (LoadWithLoader(target, &request))
return;
- CHECK(!file_url.is_null() && !base_filter.is_null());
+ CHECK(!file_url.is_null() && !capabilities_ptr.is_null());
if (target.name() != resolved_name) {
// In cases where a package alias is resolved, we have to use the instance
diff --git a/mojo/shell/shell.h b/mojo/shell/shell.h
index 7169ec1..2d66593 100644
--- a/mojo/shell/shell.h
+++ b/mojo/shell/shell.h
@@ -17,6 +17,7 @@
#include "mojo/shell/connect_params.h"
#include "mojo/shell/loader.h"
#include "mojo/shell/native_runner.h"
+#include "mojo/shell/public/cpp/capabilities.h"
#include "mojo/shell/public/cpp/identity.h"
#include "mojo/shell/public/cpp/interface_factory.h"
#include "mojo/shell/public/cpp/shell_client.h"
@@ -36,25 +37,10 @@ namespace mojo {
class ShellConnection;
namespace shell {
-// A set of names of interfaces that may be exposed to an application.
-using AllowedInterfaces = std::set<std::string>;
-// A map of allowed applications to allowed interface sets. See shell.mojom for
-// more details.
-using CapabilityFilter = std::map<std::string, AllowedInterfaces>;
-
// Creates an identity for the Shell, used when the Shell connects to
// applications.
Identity CreateShellIdentity();
-// Returns a capability filter that allows an application to connect to any
-// other application and any service exposed by other applications.
-CapabilityFilter GetPermissiveCapabilityFilter();
-
-// Returns the set of interfaces that an application instance with |filter| is
-// allowed to see from an instance with |identity|.
-AllowedInterfaces GetAllowedInterfaces(const CapabilityFilter& filter,
- const Identity& identity);
-
class Shell : public ShellClient {
public:
// API for testing.
@@ -141,17 +127,9 @@ class Shell : public ShellClient {
bool ConnectToExistingInstance(scoped_ptr<ConnectParams>* params);
Instance* CreateInstance(const Identity& target_id,
- const CapabilityFilter& filter,
+ const CapabilitySpec& capabilities,
mojom::ShellClientRequest* request);
- // Called from the instance implementing mojom::Shell. |user_id| must be
- // resolved by the instance (i.e. must not be mojom::kInheritUserID).
- void CreateInstanceForFactory(
- mojom::ShellClientFactoryPtr factory,
- const std::string& name,
- const std::string& user_id,
- mojom::CapabilityFilterPtr filter,
- mojom::PIDReceiverRequest pid_receiver);
// Called from the instance implementing mojom::Shell.
void AddInstanceListener(mojom::InstanceListenerPtr listener);
@@ -171,12 +149,12 @@ class Shell : public ShellClient {
// |resolved_name| is the mojo: name identifying the physical package
// application.
// |file_url| is the resolved file:// URL of the physical package.
- // |base_filter| is the CapabilityFilter the requested application should be
+ // |base_filter| is the CapabilitySpecPtr the requested application should be
// run with, from its manifest.
void OnGotResolvedName(scoped_ptr<ConnectParams> params,
const String& resolved_name,
const String& resolved_instance,
- mojom::CapabilityFilterPtr base_filter,
+ mojom::CapabilitySpecPtr capabilities,
const String& file_url);
// Tries to load |target| with an Loader. Returns true if one was registered
diff --git a/mojo/shell/tests/connect/connect_test_app_manifest.json b/mojo/shell/tests/connect/connect_test_app_manifest.json
index 39099fb..ae25a53 100644
--- a/mojo/shell/tests/connect/connect_test_app_manifest.json
+++ b/mojo/shell/tests/connect/connect_test_app_manifest.json
@@ -1,7 +1,10 @@
{
+ "manifest_version": 1,
"name": "mojo:connect_test_app",
"display_name": "Connect Test App",
"capabilities": {
- "mojo:connect_test_a": ["*"]
+ "required": {
+ "mojo:connect_test_a": { "interfaces": ["*"] }
+ }
}
}