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