1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/common/manifest.h"
6
7#include <algorithm>
8#include <set>
9#include <string>
10
11#include "base/memory/scoped_ptr.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/values.h"
14#include "extensions/common/error_utils.h"
15#include "extensions/common/features/feature.h"
16#include "extensions/common/features/simple_feature.h"
17#include "extensions/common/install_warning.h"
18#include "extensions/common/manifest_constants.h"
19#include "testing/gtest/include/gtest/gtest.h"
20
21namespace extensions {
22
23namespace errors = manifest_errors;
24namespace keys = manifest_keys;
25
26class ManifestTest : public testing::Test {
27 public:
28  ManifestTest() : default_value_("test") {}
29
30 protected:
31  void AssertType(Manifest* manifest, Manifest::Type type) {
32    EXPECT_EQ(type, manifest->type());
33    EXPECT_EQ(type == Manifest::TYPE_THEME, manifest->is_theme());
34    EXPECT_EQ(type == Manifest::TYPE_PLATFORM_APP,
35              manifest->is_platform_app());
36    EXPECT_EQ(type == Manifest::TYPE_LEGACY_PACKAGED_APP,
37              manifest->is_legacy_packaged_app());
38    EXPECT_EQ(type == Manifest::TYPE_HOSTED_APP, manifest->is_hosted_app());
39    EXPECT_EQ(type == Manifest::TYPE_SHARED_MODULE,
40              manifest->is_shared_module());
41  }
42
43  // Helper function that replaces the Manifest held by |manifest| with a copy
44  // with its |key| changed to |value|. If |value| is NULL, then |key| will
45  // instead be deleted.
46  void MutateManifest(scoped_ptr<Manifest>* manifest,
47                      const std::string& key,
48                      base::Value* value) {
49    scoped_ptr<base::DictionaryValue> manifest_value(
50        manifest->get()->value()->DeepCopy());
51    if (value)
52      manifest_value->Set(key, value);
53    else
54      manifest_value->Remove(key, NULL);
55    manifest->reset(new Manifest(Manifest::INTERNAL, manifest_value.Pass()));
56  }
57
58  std::string default_value_;
59};
60
61// Verifies that extensions can access the correct keys.
62TEST_F(ManifestTest, Extension) {
63  scoped_ptr<base::DictionaryValue> manifest_value(new base::DictionaryValue());
64  manifest_value->SetString(keys::kName, "extension");
65  manifest_value->SetString(keys::kVersion, "1");
66  // Only supported in manifest_version=1.
67  manifest_value->SetString(keys::kBackgroundPageLegacy, "bg.html");
68  manifest_value->SetString("unknown_key", "foo");
69
70  scoped_ptr<Manifest> manifest(
71      new Manifest(Manifest::INTERNAL, manifest_value.Pass()));
72  std::string error;
73  std::vector<InstallWarning> warnings;
74  EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
75  EXPECT_TRUE(error.empty());
76  ASSERT_EQ(1u, warnings.size());
77  AssertType(manifest.get(), Manifest::TYPE_EXTENSION);
78
79  // The known key 'background_page' should be accessible.
80  std::string value;
81  EXPECT_TRUE(manifest->GetString(keys::kBackgroundPageLegacy, &value));
82  EXPECT_EQ("bg.html", value);
83
84  // The unknown key 'unknown_key' should be accesible.
85  value.clear();
86  EXPECT_TRUE(manifest->GetString("unknown_key", &value));
87  EXPECT_EQ("foo", value);
88
89  // Set the manifest_version to 2; background_page should stop working.
90  value.clear();
91  MutateManifest(
92      &manifest, keys::kManifestVersion, new base::FundamentalValue(2));
93  EXPECT_FALSE(manifest->GetString("background_page", &value));
94  EXPECT_EQ("", value);
95
96  // Validate should also give a warning.
97  warnings.clear();
98  EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
99  EXPECT_TRUE(error.empty());
100  ASSERT_EQ(2u, warnings.size());
101  {
102    SimpleFeature feature;
103    feature.set_name("background_page");
104    feature.set_max_manifest_version(1);
105    EXPECT_EQ(
106        "'background_page' requires manifest version of 1 or lower.",
107        warnings[0].message);
108  }
109
110  // Test DeepCopy and Equals.
111  scoped_ptr<Manifest> manifest2(manifest->DeepCopy());
112  EXPECT_TRUE(manifest->Equals(manifest2.get()));
113  EXPECT_TRUE(manifest2->Equals(manifest.get()));
114  MutateManifest(
115      &manifest, "foo", new base::StringValue("blah"));
116  EXPECT_FALSE(manifest->Equals(manifest2.get()));
117}
118
119// Verifies that key restriction based on type works.
120TEST_F(ManifestTest, ExtensionTypes) {
121  scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
122  value->SetString(keys::kName, "extension");
123  value->SetString(keys::kVersion, "1");
124
125  scoped_ptr<Manifest> manifest(
126      new Manifest(Manifest::INTERNAL, value.Pass()));
127  std::string error;
128  std::vector<InstallWarning> warnings;
129  EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
130  EXPECT_TRUE(error.empty());
131  EXPECT_TRUE(warnings.empty());
132
133  // By default, the type is Extension.
134  AssertType(manifest.get(), Manifest::TYPE_EXTENSION);
135
136  // Theme.
137  MutateManifest(
138      &manifest, keys::kTheme, new base::DictionaryValue());
139  AssertType(manifest.get(), Manifest::TYPE_THEME);
140  MutateManifest(
141      &manifest, keys::kTheme, NULL);
142
143  // Shared module.
144  MutateManifest(
145      &manifest, keys::kExport, new base::DictionaryValue());
146  AssertType(manifest.get(), Manifest::TYPE_SHARED_MODULE);
147  MutateManifest(
148      &manifest, keys::kExport, NULL);
149
150  // Packaged app.
151  MutateManifest(
152      &manifest, keys::kApp, new base::DictionaryValue());
153  AssertType(manifest.get(), Manifest::TYPE_LEGACY_PACKAGED_APP);
154
155  // Platform app with event page.
156  MutateManifest(
157      &manifest, keys::kPlatformAppBackground, new base::DictionaryValue());
158  AssertType(manifest.get(), Manifest::TYPE_PLATFORM_APP);
159  MutateManifest(
160      &manifest, keys::kPlatformAppBackground, NULL);
161
162  // Platform app with service worker.
163  MutateManifest(
164      &manifest, keys::kPlatformAppServiceWorker, new base::DictionaryValue());
165  AssertType(manifest.get(), Manifest::TYPE_PLATFORM_APP);
166  MutateManifest(&manifest, keys::kPlatformAppServiceWorker, NULL);
167
168  // Hosted app.
169  MutateManifest(
170      &manifest, keys::kWebURLs, new base::ListValue());
171  AssertType(manifest.get(), Manifest::TYPE_HOSTED_APP);
172  MutateManifest(
173      &manifest, keys::kWebURLs, NULL);
174  MutateManifest(
175      &manifest, keys::kLaunchWebURL, new base::StringValue("foo"));
176  AssertType(manifest.get(), Manifest::TYPE_HOSTED_APP);
177  MutateManifest(
178      &manifest, keys::kLaunchWebURL, NULL);
179};
180
181// Verifies that the getters filter restricted keys.
182TEST_F(ManifestTest, RestrictedKeys) {
183  scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
184  value->SetString(keys::kName, "extension");
185  value->SetString(keys::kVersion, "1");
186
187  scoped_ptr<Manifest> manifest(
188      new Manifest(Manifest::INTERNAL, value.Pass()));
189  std::string error;
190  std::vector<InstallWarning> warnings;
191  EXPECT_TRUE(manifest->ValidateManifest(&error, &warnings));
192  EXPECT_TRUE(error.empty());
193  EXPECT_TRUE(warnings.empty());
194
195  // "Commands" requires manifest version 2.
196  const base::Value* output = NULL;
197  MutateManifest(
198      &manifest, keys::kCommands, new base::DictionaryValue());
199  EXPECT_FALSE(manifest->HasKey(keys::kCommands));
200  EXPECT_FALSE(manifest->Get(keys::kCommands, &output));
201
202  MutateManifest(
203      &manifest, keys::kManifestVersion, new base::FundamentalValue(2));
204  EXPECT_TRUE(manifest->HasKey(keys::kCommands));
205  EXPECT_TRUE(manifest->Get(keys::kCommands, &output));
206
207  MutateManifest(
208      &manifest, keys::kPageAction, new base::DictionaryValue());
209  AssertType(manifest.get(), Manifest::TYPE_EXTENSION);
210  EXPECT_TRUE(manifest->HasKey(keys::kPageAction));
211  EXPECT_TRUE(manifest->Get(keys::kPageAction, &output));
212
213  // Platform apps cannot have a "page_action" key.
214  MutateManifest(
215      &manifest, keys::kPlatformAppBackground, new base::DictionaryValue());
216  AssertType(manifest.get(), Manifest::TYPE_PLATFORM_APP);
217  EXPECT_FALSE(manifest->HasKey(keys::kPageAction));
218  EXPECT_FALSE(manifest->Get(keys::kPageAction, &output));
219  MutateManifest(
220      &manifest, keys::kPlatformAppBackground, NULL);
221
222  // Platform apps also can't have a "Commands" key.
223  EXPECT_FALSE(manifest->HasKey(keys::kCommands));
224  EXPECT_FALSE(manifest->Get(keys::kCommands, &output));
225};
226
227}  // namespace extensions
228