1// Copyright 2014 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/features/base_feature_provider.h"
6
7#include "chrome/common/extensions/features/chrome_channel_feature_filter.h"
8#include "chrome/common/extensions/features/feature_channel.h"
9#include "extensions/common/features/permission_feature.h"
10#include "extensions/common/value_builder.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13using chrome::VersionInfo;
14
15namespace extensions {
16
17namespace {
18
19template <class FeatureClass>
20SimpleFeature* CreateFeature() {
21  SimpleFeature* feature = new FeatureClass();
22  feature->AddFilter(
23      scoped_ptr<SimpleFeatureFilter>(new ChromeChannelFeatureFilter(feature)));
24  return feature;
25}
26
27}  // namespace
28
29TEST(BaseFeatureProviderTest, ManifestFeatures) {
30  const FeatureProvider* provider = BaseFeatureProvider::GetByName("manifest");
31  SimpleFeature* feature =
32      static_cast<SimpleFeature*>(provider->GetFeature("description"));
33  ASSERT_TRUE(feature);
34  EXPECT_EQ(6u, feature->extension_types()->size());
35  EXPECT_EQ(1u, feature->extension_types()->count(Manifest::TYPE_EXTENSION));
36  EXPECT_EQ(1u,
37      feature->extension_types()->count(Manifest::TYPE_LEGACY_PACKAGED_APP));
38  EXPECT_EQ(1u,
39            feature->extension_types()->count(Manifest::TYPE_PLATFORM_APP));
40  EXPECT_EQ(1u, feature->extension_types()->count(Manifest::TYPE_HOSTED_APP));
41  EXPECT_EQ(1u, feature->extension_types()->count(Manifest::TYPE_THEME));
42  EXPECT_EQ(1u,
43            feature->extension_types()->count(Manifest::TYPE_SHARED_MODULE));
44
45  base::DictionaryValue manifest;
46  manifest.SetString("name", "test extension");
47  manifest.SetString("version", "1");
48  manifest.SetString("description", "hello there");
49
50  std::string error;
51  scoped_refptr<const Extension> extension(Extension::Create(
52      base::FilePath(), Manifest::INTERNAL, manifest, Extension::NO_FLAGS,
53      &error));
54
55  ASSERT_TRUE(extension.get());
56  EXPECT_EQ(Feature::IS_AVAILABLE, feature->IsAvailableToContext(
57      extension.get(), Feature::UNSPECIFIED_CONTEXT).result());
58
59  feature =
60    static_cast<SimpleFeature*>(provider->GetFeature("theme"));
61  ASSERT_TRUE(feature);
62  EXPECT_EQ(Feature::INVALID_TYPE, feature->IsAvailableToContext(
63      extension.get(), Feature::UNSPECIFIED_CONTEXT).result());
64
65  feature =
66    static_cast<SimpleFeature*>(provider->GetFeature("devtools_page"));
67  ASSERT_TRUE(feature);
68  EXPECT_EQ(Feature::NOT_PRESENT, feature->IsAvailableToContext(
69      extension.get(), Feature::UNSPECIFIED_CONTEXT).result());
70}
71
72TEST(BaseFeatureProviderTest, PermissionFeatures) {
73  const FeatureProvider* provider =
74      BaseFeatureProvider::GetByName("permission");
75  SimpleFeature* feature =
76      static_cast<SimpleFeature*>(provider->GetFeature("contextMenus"));
77  ASSERT_TRUE(feature);
78  EXPECT_EQ(3u, feature->extension_types()->size());
79  EXPECT_EQ(1u, feature->extension_types()->count(Manifest::TYPE_EXTENSION));
80  EXPECT_EQ(1u,
81      feature->extension_types()->count(Manifest::TYPE_LEGACY_PACKAGED_APP));
82  EXPECT_EQ(1u,
83            feature->extension_types()->count(Manifest::TYPE_PLATFORM_APP));
84
85  base::DictionaryValue manifest;
86  manifest.SetString("name", "test extension");
87  manifest.SetString("version", "1");
88  base::ListValue* permissions = new base::ListValue();
89  manifest.Set("permissions", permissions);
90  permissions->Append(new base::StringValue("contextMenus"));
91
92  std::string error;
93  scoped_refptr<const Extension> extension(Extension::Create(
94      base::FilePath(), Manifest::INTERNAL, manifest, Extension::NO_FLAGS,
95      &error));
96
97  ASSERT_TRUE(extension.get());
98  EXPECT_EQ(Feature::IS_AVAILABLE, feature->IsAvailableToContext(
99      extension.get(), Feature::UNSPECIFIED_CONTEXT).result());
100
101  feature =
102    static_cast<SimpleFeature*>(provider->GetFeature("chromePrivate"));
103  ASSERT_TRUE(feature);
104  EXPECT_EQ(Feature::NOT_FOUND_IN_WHITELIST, feature->IsAvailableToContext(
105      extension.get(), Feature::UNSPECIFIED_CONTEXT).result());
106
107  feature =
108    static_cast<SimpleFeature*>(provider->GetFeature("clipboardWrite"));
109  ASSERT_TRUE(feature);
110  EXPECT_EQ(Feature::NOT_PRESENT, feature->IsAvailableToContext(
111      extension.get(), Feature::UNSPECIFIED_CONTEXT).result());
112}
113
114TEST(BaseFeatureProviderTest, Validation) {
115  scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
116
117  base::DictionaryValue* feature1 = new base::DictionaryValue();
118  feature1->SetString("channel", "trunk");
119  value->Set("feature1", feature1);
120
121  base::DictionaryValue* feature2 = new base::DictionaryValue();
122  feature2->SetString("channel", "trunk");
123  base::ListValue* extension_types = new base::ListValue();
124  extension_types->Append(new base::StringValue("extension"));
125  feature2->Set("extension_types", extension_types);
126  base::ListValue* contexts = new base::ListValue();
127  contexts->Append(new base::StringValue("blessed_extension"));
128  feature2->Set("contexts", contexts);
129  value->Set("feature2", feature2);
130
131  scoped_ptr<BaseFeatureProvider> provider(
132      new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>));
133
134  // feature1 won't validate because it lacks an extension type.
135  EXPECT_FALSE(provider->GetFeature("feature1"));
136
137  // If we add one, it works.
138  feature1->Set("extension_types", extension_types->DeepCopy());
139  provider.reset(
140      new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>));
141  EXPECT_TRUE(provider->GetFeature("feature1"));
142
143  // Remove the channel, and feature1 won't validate.
144  feature1->Remove("channel", NULL);
145  provider.reset(
146      new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>));
147  EXPECT_FALSE(provider->GetFeature("feature1"));
148
149  // feature2 won't validate because of the presence of "contexts".
150  EXPECT_FALSE(provider->GetFeature("feature2"));
151
152  // If we remove it, it works.
153  feature2->Remove("contexts", NULL);
154  provider.reset(
155      new BaseFeatureProvider(*value, CreateFeature<PermissionFeature>));
156  EXPECT_TRUE(provider->GetFeature("feature2"));
157}
158
159TEST(BaseFeatureProviderTest, ComplexFeatures) {
160  scoped_ptr<base::DictionaryValue> rule(
161      DictionaryBuilder()
162      .Set("feature1", ListBuilder()
163          .Append(DictionaryBuilder()
164              .Set("channel", "beta")
165              .Set("extension_types", ListBuilder()
166                  .Append("extension")))
167                  .Append(DictionaryBuilder()
168                      .Set("channel", "beta")
169                      .Set("extension_types", ListBuilder()
170                          .Append("legacy_packaged_app"))))
171      .Build());
172
173  scoped_ptr<BaseFeatureProvider> provider(
174      new BaseFeatureProvider(*rule, CreateFeature<SimpleFeature>));
175
176  Feature* feature = provider->GetFeature("feature1");
177  EXPECT_TRUE(feature);
178
179  // Make sure both rules are applied correctly.
180  {
181    ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_BETA);
182    EXPECT_EQ(
183        Feature::IS_AVAILABLE,
184        feature->IsAvailableToManifest("1",
185                                       Manifest::TYPE_EXTENSION,
186                                       Manifest::INVALID_LOCATION,
187                                       Feature::UNSPECIFIED_PLATFORM).result());
188    EXPECT_EQ(
189        Feature::IS_AVAILABLE,
190        feature->IsAvailableToManifest("2",
191                                       Manifest::TYPE_LEGACY_PACKAGED_APP,
192                                       Manifest::INVALID_LOCATION,
193                                       Feature::UNSPECIFIED_PLATFORM).result());
194  }
195  {
196    ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_STABLE);
197    EXPECT_NE(
198        Feature::IS_AVAILABLE,
199        feature->IsAvailableToManifest("1",
200                                       Manifest::TYPE_EXTENSION,
201                                       Manifest::INVALID_LOCATION,
202                                       Feature::UNSPECIFIED_PLATFORM).result());
203    EXPECT_NE(
204        Feature::IS_AVAILABLE,
205        feature->IsAvailableToManifest("2",
206                                       Manifest::TYPE_LEGACY_PACKAGED_APP,
207                                       Manifest::INVALID_LOCATION,
208                                       Feature::UNSPECIFIED_PLATFORM).result());
209  }
210}
211
212}  // namespace extensions
213