summaryrefslogtreecommitdiffstats
path: root/mojo
diff options
context:
space:
mode:
authorben <ben@chromium.org>2016-03-08 22:50:53 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-09 06:51:48 +0000
commita6d912093a6d1123c1d8dfb3a0ca3802d03a0f2d (patch)
tree589e366a415d4812af25af873ca96dc8ddff5d82 /mojo
parentb98777bcf33f467bba92cff7de951f0b3fd13b32 (diff)
downloadchromium_src-a6d912093a6d1123c1d8dfb3a0ca3802d03a0f2d.zip
chromium_src-a6d912093a6d1123c1d8dfb3a0ca3802d03a0f2d.tar.gz
chromium_src-a6d912093a6d1123c1d8dfb3a0ca3802d03a0f2d.tar.bz2
Add catalog unittests.
- Decompose the catalog into various files. TBR=sky@chromium.org BUG= Review URL: https://codereview.chromium.org/1779683002 Cr-Commit-Position: refs/heads/master@{#380093}
Diffstat (limited to 'mojo')
-rw-r--r--mojo/mojo_shell.gyp10
-rw-r--r--mojo/services/catalog/BUILD.gn21
-rw-r--r--mojo/services/catalog/builder.cc64
-rw-r--r--mojo/services/catalog/builder.h22
-rw-r--r--mojo/services/catalog/builder_unittest.cc89
-rw-r--r--mojo/services/catalog/catalog.cc124
-rw-r--r--mojo/services/catalog/catalog.h56
-rw-r--r--mojo/services/catalog/data/capabilities8
-rw-r--r--mojo/services/catalog/data/instance6
-rw-r--r--mojo/services/catalog/data/malformed4
-rw-r--r--mojo/services/catalog/data/simple5
-rw-r--r--mojo/services/catalog/entry.cc13
-rw-r--r--mojo/services/catalog/entry.h34
-rw-r--r--mojo/services/catalog/loader.h2
-rw-r--r--mojo/services/catalog/store.cc18
-rw-r--r--mojo/services/catalog/store.h42
-rw-r--r--mojo/shell/background/tests/test_catalog_store.h2
-rw-r--r--mojo/shell/tests/BUILD.gn1
18 files changed, 375 insertions, 146 deletions
diff --git a/mojo/mojo_shell.gyp b/mojo/mojo_shell.gyp
index beabed2..35192b1 100644
--- a/mojo/mojo_shell.gyp
+++ b/mojo/mojo_shell.gyp
@@ -7,10 +7,16 @@
'target_name': 'mojo_shell_lib',
'type': 'static_library',
'sources': [
- 'services/catalog/loader.cc',
- 'services/catalog/loader.h',
+ 'services/catalog/builder.cc',
+ 'services/catalog/builder.h',
'services/catalog/catalog.cc',
'services/catalog/catalog.h',
+ 'services/catalog/entry.cc',
+ 'services/catalog/entry.h',
+ 'services/catalog/loader.cc',
+ 'services/catalog/loader.h',
+ 'services/catalog/store.cc',
+ 'services/catalog/store.h',
'shell/loader.h',
'shell/connect_params.cc',
'shell/connect_params.h',
diff --git a/mojo/services/catalog/BUILD.gn b/mojo/services/catalog/BUILD.gn
index e1abca9..9dd285a 100644
--- a/mojo/services/catalog/BUILD.gn
+++ b/mojo/services/catalog/BUILD.gn
@@ -4,8 +4,10 @@
import("//mojo/public/mojo_application.gni")
import("//mojo/public/mojo_application_manifest.gni")
+import("//testing/test.gni")
group("catalog") {
+ testonly = true
deps = [
":lib",
]
@@ -13,10 +15,16 @@ group("catalog") {
source_set("lib") {
sources = [
+ "builder.cc",
+ "builder.h",
"catalog.cc",
"catalog.h",
+ "entry.cc",
+ "entry.h",
"loader.cc",
"loader.h",
+ "store.cc",
+ "store.h",
]
deps = [
@@ -34,3 +42,16 @@ mojo_application_manifest("manifest") {
application_name = "catalog"
source = "manifest.json"
}
+
+source_set("unittests") {
+ testonly = true
+ sources = [
+ "builder_unittest.cc",
+ ]
+ deps = [
+ ":lib",
+ "//base",
+ "//mojo/shell/public/cpp",
+ "//testing/gtest",
+ ]
+}
diff --git a/mojo/services/catalog/builder.cc b/mojo/services/catalog/builder.cc
new file mode 100644
index 0000000..cd011120
--- /dev/null
+++ b/mojo/services/catalog/builder.cc
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/services/catalog/builder.h"
+
+#include "base/values.h"
+#include "mojo/services/catalog/store.h"
+#include "mojo/shell/public/cpp/names.h"
+
+namespace catalog {
+
+CapabilityFilter BuildCapabilityFilter(const base::DictionaryValue& value) {
+ CapabilityFilter filter;
+ base::DictionaryValue::Iterator it(value);
+ for (; !it.IsAtEnd(); it.Advance()) {
+ const base::ListValue* values = nullptr;
+ CHECK(it.value().GetAsList(&values));
+ AllowedInterfaces interfaces;
+ for (auto i = values->begin(); i != values->end(); ++i) {
+ std::string iface_name;
+ const base::Value* v = *i;
+ CHECK(v->GetAsString(&iface_name));
+ interfaces.insert(iface_name);
+ }
+ filter[it.key()] = interfaces;
+ }
+ return filter;
+}
+
+Entry BuildEntry(const base::DictionaryValue& value) {
+ Entry entry;
+ std::string name_string;
+ CHECK(value.GetString(Store::kNameKey, &name_string));
+ CHECK(mojo::IsValidName(name_string)) << "Invalid Name: " << name_string;
+ entry.name = name_string;
+ if (value.HasKey(Store::kQualifierKey)) {
+ CHECK(value.GetString(Store::kQualifierKey, &entry.qualifier));
+ } else {
+ entry.qualifier = mojo::GetNamePath(name_string);
+ }
+ CHECK(value.GetString(Store::kDisplayNameKey, &entry.display_name));
+ const base::DictionaryValue* capabilities = nullptr;
+ CHECK(value.GetDictionary(Store::kCapabilitiesKey, &capabilities));
+ entry.capabilities = BuildCapabilityFilter(*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::ListValue> interfaces(new base::ListValue);
+ for (const auto& iface_name : pair.second)
+ interfaces->AppendString(iface_name);
+ capabilities->Set(pair.first, std::move(interfaces));
+ }
+ (*value)->Set(Store::kCapabilitiesKey, make_scoped_ptr(capabilities));
+}
+
+} // namespace catalog
diff --git a/mojo/services/catalog/builder.h b/mojo/services/catalog/builder.h
new file mode 100644
index 0000000..2f3e1aa
--- /dev/null
+++ b/mojo/services/catalog/builder.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 MOJO_SERVICES_CATALOG_BUILDER_H_
+#define MOJO_SERVICES_CATALOG_BUILDER_H_
+
+#include "mojo/services/catalog/entry.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace catalog {
+
+Entry BuildEntry(const base::DictionaryValue& value);
+
+void SerializeEntry(const Entry& entry, base::DictionaryValue** value);
+
+} // namespace catalog
+
+#endif // MOJO_SERVICES_CATALOG_BUILDER_H_
diff --git a/mojo/services/catalog/builder_unittest.cc b/mojo/services/catalog/builder_unittest.cc
new file mode 100644
index 0000000..4290a2f
--- /dev/null
+++ b/mojo/services/catalog/builder_unittest.cc
@@ -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.
+
+#include "mojo/services/catalog/builder.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 "mojo/shell/public/cpp/names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class BuilderTest : public testing::Test {
+ public:
+ BuilderTest() {}
+ ~BuilderTest() override {}
+
+ protected:
+ scoped_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(
+ "mojo/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(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);
+
+ EXPECT_EQ("mojo:foo", entry.name);
+ EXPECT_EQ(mojo::GetNamePath(entry.name), entry.qualifier);
+ EXPECT_EQ("Foo", entry.display_name);
+}
+
+TEST_F(BuilderTest, Instance) {
+ scoped_ptr<base::Value> value = ReadManifest("instance");
+ base::DictionaryValue* dictionary = nullptr;
+ CHECK(value->GetAsDictionary(&dictionary));
+ Entry entry = BuildEntry(*dictionary);
+
+ EXPECT_EQ("mojo:foo", entry.name);
+ EXPECT_EQ("bar", entry.qualifier);
+ EXPECT_EQ("Foo", entry.display_name);
+}
+
+TEST_F(BuilderTest, Capabilities) {
+ scoped_ptr<base::Value> value = ReadManifest("capabilities");
+ base::DictionaryValue* dictionary = nullptr;
+ CHECK(value->GetAsDictionary(&dictionary));
+ Entry entry = BuildEntry(*dictionary);
+
+ 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);
+}
+
+TEST_F(BuilderTest, Malformed) {
+ scoped_ptr<base::Value> value = ReadManifest("malformed");
+ EXPECT_FALSE(value.get());
+}
+
+
+} // namespace catalog
diff --git a/mojo/services/catalog/catalog.cc b/mojo/services/catalog/catalog.cc
index 6e7423a..6d5518c 100644
--- a/mojo/services/catalog/catalog.cc
+++ b/mojo/services/catalog/catalog.cc
@@ -9,6 +9,9 @@
#include "base/strings/string_split.h"
#include "base/task_runner_util.h"
#include "mojo/common/url_type_converters.h"
+#include "mojo/services/catalog/builder.h"
+#include "mojo/services/catalog/entry.h"
+#include "mojo/services/catalog/store.h"
#include "mojo/shell/public/cpp/names.h"
#include "mojo/util/filename_util.h"
#include "net/base/filename_util.h"
@@ -17,59 +20,6 @@
namespace catalog {
namespace {
-CapabilityFilter BuildCapabilityFilterFromDictionary(
- const base::DictionaryValue& value) {
- CapabilityFilter filter;
- base::DictionaryValue::Iterator it(value);
- for (; !it.IsAtEnd(); it.Advance()) {
- const base::ListValue* values = nullptr;
- CHECK(it.value().GetAsList(&values));
- AllowedInterfaces interfaces;
- for (auto i = values->begin(); i != values->end(); ++i) {
- std::string iface_name;
- const base::Value* v = *i;
- CHECK(v->GetAsString(&iface_name));
- interfaces.insert(iface_name);
- }
- filter[it.key()] = interfaces;
- }
- return filter;
-}
-
-ApplicationInfo BuildApplicationInfoFromDictionary(
- const base::DictionaryValue& value) {
- ApplicationInfo info;
- std::string name_string;
- CHECK(value.GetString(Store::kNameKey, &name_string));
- CHECK(mojo::IsValidName(name_string)) << "Invalid Name: " << name_string;
- info.name = name_string;
- if (value.HasKey(Store::kQualifierKey)) {
- CHECK(value.GetString(Store::kQualifierKey, &info.qualifier));
- } else {
- info.qualifier = mojo::GetNamePath(name_string);
- }
- CHECK(value.GetString(Store::kDisplayNameKey, &info.display_name));
- const base::DictionaryValue* capabilities = nullptr;
- CHECK(value.GetDictionary(Store::kCapabilitiesKey, &capabilities));
- info.base_filter = BuildCapabilityFilterFromDictionary(*capabilities);
- return info;
-}
-
-void SerializeEntry(const ApplicationInfo& 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.base_filter) {
- 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));
- }
- (*value)->Set(Store::kCapabilitiesKey, make_scoped_ptr(capabilities));
-}
-
scoped_ptr<base::Value> ReadManifest(const base::FilePath& manifest_path) {
JSONFileValueDeserializer deserializer(manifest_path);
int error = 0;
@@ -81,19 +31,6 @@ scoped_ptr<base::Value> ReadManifest(const base::FilePath& manifest_path) {
} // namespace
-// 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";
-
-ApplicationInfo::ApplicationInfo() {}
-ApplicationInfo::ApplicationInfo(const ApplicationInfo& other) = default;
-ApplicationInfo::~ApplicationInfo() {}
-
Catalog::Catalog(base::TaskRunner* blocking_pool,
scoped_ptr<Store> catalog)
: blocking_pool_(blocking_pool),
@@ -180,10 +117,10 @@ void Catalog::GetEntries(mojo::Array<mojo::String> names,
for (const std::string& name : names_vec) {
if (catalog_.find(name) == catalog_.end())
continue;
- const ApplicationInfo& info = catalog_[name];
- mojom::CatalogEntryPtr entry(mojom::CatalogEntry::New());
- entry->display_name = info.display_name;
- entries[info.name] = std::move(entry);
+ const Entry& entry = catalog_[name];
+ mojom::CatalogEntryPtr entry_ptr(mojom::CatalogEntry::New());
+ entry_ptr->display_name = entry.display_name;
+ entries[entry.name] = std::move(entry_ptr);
}
callback.Run(std::move(entries));
}
@@ -192,8 +129,8 @@ void Catalog::CompleteResolveMojoName(
const std::string& resolved_name,
const std::string& qualifier,
const ResolveMojoNameCallback& callback) {
- auto info_iter = catalog_.find(resolved_name);
- CHECK(info_iter != catalog_.end());
+ auto entry_iter = catalog_.find(resolved_name);
+ CHECK(entry_iter != catalog_.end());
GURL file_url;
std::string type = mojo::GetNameType(resolved_name);
@@ -214,7 +151,7 @@ void Catalog::CompleteResolveMojoName(
mojo::shell::mojom::CapabilityFilterPtr filter(
mojo::shell::mojom::CapabilityFilter::New());
filter->filter = mojo::Map<mojo::String, mojo::Array<mojo::String>>();
- for (const auto& entry : info_iter->second.base_filter) {
+ for (const auto& entry : entry_iter->second.capabilities) {
mojo::Array<mojo::String> interfaces;
for (auto interface_name : entry.second)
interfaces.push_back(interface_name);
@@ -259,43 +196,42 @@ void Catalog::DeserializeCatalog() {
const base::DictionaryValue* dictionary = nullptr;
const base::Value* v = *it;
CHECK(v->GetAsDictionary(&dictionary));
- const ApplicationInfo app_info =
- BuildApplicationInfoFromDictionary(*dictionary);
- catalog_[app_info.name] = app_info;
+ const Entry entry = BuildEntry(*dictionary);
+ catalog_[entry.name] = entry;
}
}
void Catalog::SerializeCatalog() {
scoped_ptr<base::ListValue> catalog(new base::ListValue);
- for (const auto& info : catalog_) {
+ for (const auto& entry : catalog_) {
base::DictionaryValue* dictionary = nullptr;
- SerializeEntry(info.second, &dictionary);
+ SerializeEntry(entry.second, &dictionary);
catalog->Append(make_scoped_ptr(dictionary));
}
if (store_)
store_->UpdateStore(std::move(catalog));
}
-const ApplicationInfo& Catalog::DeserializeApplication(
+const Entry& Catalog::DeserializeApplication(
const base::DictionaryValue* dictionary) {
- ApplicationInfo info = BuildApplicationInfoFromDictionary(*dictionary);
- if (catalog_.find(info.name) == catalog_.end()) {
- catalog_[info.name] = info;
+ Entry entry = BuildEntry(*dictionary);
+ if (catalog_.find(entry.name) == catalog_.end()) {
+ catalog_[entry.name] = entry;
if (dictionary->HasKey("applications")) {
const base::ListValue* applications = nullptr;
dictionary->GetList("applications", &applications);
for (size_t i = 0; i < applications->GetSize(); ++i) {
- const base::DictionaryValue* child = nullptr;
- applications->GetDictionary(i, &child);
- const ApplicationInfo& child_info = DeserializeApplication(child);
- mojo_name_aliases_[child_info.name] =
- std::make_pair(info.name, child_info.qualifier);
+ const base::DictionaryValue* child_value = nullptr;
+ applications->GetDictionary(i, &child_value);
+ const Entry& child = DeserializeApplication(child_value);
+ mojo_name_aliases_[child.name] =
+ std::make_pair(entry.name, child.qualifier);
}
}
- qualifiers_[info.name] = info.qualifier;
+ qualifiers_[entry.name] = entry.qualifier;
}
- return catalog_[info.name];
+ return catalog_[entry.name];
}
GURL Catalog::GetManifestURL(const std::string& name) {
@@ -331,11 +267,11 @@ void Catalog::OnReadManifestImpl(const std::string& name,
CHECK(manifest->GetAsDictionary(&dictionary));
DeserializeApplication(dictionary);
} else {
- ApplicationInfo info;
- info.name = name;
- info.display_name = name;
- catalog_[info.name] = info;
- qualifiers_[info.name] = mojo::GetNamePath(name);
+ Entry entry;
+ entry.name = name;
+ entry.display_name = name;
+ catalog_[entry.name] = entry;
+ qualifiers_[entry.name] = mojo::GetNamePath(name);
}
SerializeCatalog();
diff --git a/mojo/services/catalog/catalog.h b/mojo/services/catalog/catalog.h
index 528a2c0..06ae712 100644
--- a/mojo/services/catalog/catalog.h
+++ b/mojo/services/catalog/catalog.h
@@ -10,59 +10,18 @@
#include "base/path_service.h"
#include "base/values.h"
#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/services/catalog/entry.h"
#include "mojo/services/catalog/public/interfaces/catalog.mojom.h"
#include "mojo/services/catalog/public/interfaces/resolver.mojom.h"
+#include "mojo/services/catalog/store.h"
#include "mojo/shell/public/cpp/interface_factory.h"
#include "mojo/shell/public/cpp/shell_client.h"
#include "mojo/shell/public/interfaces/shell_resolver.mojom.h"
#include "url/gurl.h"
namespace catalog {
-// 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>;
-
-// Static information about an application package known to the Catalog.
-struct ApplicationInfo {
- ApplicationInfo();
- ApplicationInfo(const ApplicationInfo& other);
- ~ApplicationInfo();
-
- std::string name;
- std::string qualifier;
- std::string display_name;
- CapabilityFilter base_filter;
-};
-// 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 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.
- static const char kCapabilitiesKey[];
-
- 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(scoped_ptr<base::ListValue> store) = 0;
-};
+class Store;
class Catalog
: public mojo::ShellClient,
@@ -75,8 +34,8 @@ class Catalog
public:
// If |register_schemes| is true, mojo: and exe: schemes are registered as
// "standard".
- Catalog(base::TaskRunner* blocking_pool, scoped_ptr<Store> store);
- ~Catalog() override;
+ Catalog(base::TaskRunner* blocking_pool, scoped_ptr<Store> store);
+ ~Catalog() override;
private:
using MojoNameAliasMap =
@@ -136,8 +95,7 @@ class Catalog
void SerializeCatalog();
// Construct a catalog entry from |dictionary|.
- const ApplicationInfo& DeserializeApplication(
- const base::DictionaryValue* dictionary);
+ const Entry& DeserializeApplication(const base::DictionaryValue* dictionary);
GURL GetManifestURL(const std::string& name);
@@ -161,7 +119,7 @@ class Catalog
mojo::BindingSet<mojom::Catalog> catalog_bindings_;
scoped_ptr<Store> store_;
- std::map<std::string, ApplicationInfo> catalog_;
+ std::map<std::string, Entry> catalog_;
// Used when an app handles multiple names. Maps from app (as name) to name of
// app that is responsible for handling it. The value is a pair of the name of
diff --git a/mojo/services/catalog/data/capabilities b/mojo/services/catalog/data/capabilities
new file mode 100644
index 0000000..92649b2
--- /dev/null
+++ b/mojo/services/catalog/data/capabilities
@@ -0,0 +1,8 @@
+{
+ "name": "mojo:foo",
+ "display_name": "Foo",
+ "process-group": "bar",
+ "capabilities": {
+ "mojo:bar": ["mojo::Bar"]
+ }
+}
diff --git a/mojo/services/catalog/data/instance b/mojo/services/catalog/data/instance
new file mode 100644
index 0000000..41cc63d
--- /dev/null
+++ b/mojo/services/catalog/data/instance
@@ -0,0 +1,6 @@
+{
+ "name": "mojo:foo",
+ "display_name": "Foo",
+ "process-group": "bar",
+ "capabilities": {}
+}
diff --git a/mojo/services/catalog/data/malformed b/mojo/services/catalog/data/malformed
new file mode 100644
index 0000000..838a990f
--- /dev/null
+++ b/mojo/services/catalog/data/malformed
@@ -0,0 +1,4 @@
+{
+ "name": "mojo:foo",
+ "display_name": "Foo",
+ "process-group": "bar",
diff --git a/mojo/services/catalog/data/simple b/mojo/services/catalog/data/simple
new file mode 100644
index 0000000..cb60e67
--- /dev/null
+++ b/mojo/services/catalog/data/simple
@@ -0,0 +1,5 @@
+{
+ "name": "mojo:foo",
+ "display_name": "Foo",
+ "capabilities": {}
+}
diff --git a/mojo/services/catalog/entry.cc b/mojo/services/catalog/entry.cc
new file mode 100644
index 0000000..9ed445a
--- /dev/null
+++ b/mojo/services/catalog/entry.cc
@@ -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.
+
+#include "mojo/services/catalog/entry.h"
+
+namespace catalog {
+
+Entry::Entry() {}
+Entry::Entry(const Entry& other) = default;
+Entry::~Entry() {}
+
+} // catalog
diff --git a/mojo/services/catalog/entry.h b/mojo/services/catalog/entry.h
new file mode 100644
index 0000000..4f2f64a
--- /dev/null
+++ b/mojo/services/catalog/entry.h
@@ -0,0 +1,34 @@
+// 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_SERVICES_CATALOG_ENTRY_H_
+#define MOJO_SERVICES_CATALOG_ENTRY_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+namespace catalog {
+
+// 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>;
+
+// Static information about an application package known to the Catalog.
+struct Entry {
+ Entry();
+ Entry(const Entry& other);
+ ~Entry();
+
+ std::string name;
+ std::string qualifier;
+ std::string display_name;
+ CapabilityFilter capabilities;
+};
+
+} // namespace catalog
+
+#endif // MOJO_SERVICES_CATALOG_ENTRY_H_
diff --git a/mojo/services/catalog/loader.h b/mojo/services/catalog/loader.h
index 48986e1..ad02411 100644
--- a/mojo/services/catalog/loader.h
+++ b/mojo/services/catalog/loader.h
@@ -5,6 +5,8 @@
#ifndef MOJO_SERVICES_CATALOG_LOADER_H_
#define MOJO_SERVICES_CATALOG_LOADER_H_
+#include "base/memory/scoped_ptr.h"
+#include "mojo/services/catalog/store.h"
#include "mojo/shell/loader.h"
namespace base {
diff --git a/mojo/services/catalog/store.cc b/mojo/services/catalog/store.cc
new file mode 100644
index 0000000..3bce188
--- /dev/null
+++ b/mojo/services/catalog/store.cc
@@ -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.
+
+#include "mojo/services/catalog/store.h"
+
+namespace catalog {
+
+// 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";
+
+} // namespace catalog
diff --git a/mojo/services/catalog/store.h b/mojo/services/catalog/store.h
new file mode 100644
index 0000000..74309b6
--- /dev/null
+++ b/mojo/services/catalog/store.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 MOJO_SERVICES_CATALOG_STORE_H_
+#define MOJO_SERVICES_CATALOG_STORE_H_
+
+#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 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.
+ static const char kCapabilitiesKey[];
+
+ 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(scoped_ptr<base::ListValue> store) = 0;
+};
+
+} // namespace catalog
+
+#endif // MOJO_SERVICES_CATALOG_STORE_H_
diff --git a/mojo/shell/background/tests/test_catalog_store.h b/mojo/shell/background/tests/test_catalog_store.h
index 9793798..30872a1 100644
--- a/mojo/shell/background/tests/test_catalog_store.h
+++ b/mojo/shell/background/tests/test_catalog_store.h
@@ -9,7 +9,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
-#include "mojo/services/catalog/catalog.h"
+#include "mojo/services/catalog/store.h"
namespace mojo {
namespace shell {
diff --git a/mojo/shell/tests/BUILD.gn b/mojo/shell/tests/BUILD.gn
index 008eff4..37283ac 100644
--- a/mojo/shell/tests/BUILD.gn
+++ b/mojo/shell/tests/BUILD.gn
@@ -26,6 +26,7 @@ test("mojo_shell_unittests") {
"//base/test:test_support",
"//mojo/edk/system",
"//mojo/public/cpp/system",
+ "//mojo/services/catalog:unittests",
"//mojo/shell",
"//mojo/shell/background:lib",
"//mojo/shell/background:main",