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/complex_feature.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/api_feature.h" 10 #include "extensions/common/features/simple_feature.h" 11 #include "extensions/common/test_util.h" 12 #include "extensions/common/value_builder.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 using chrome::VersionInfo; 16 using extensions::APIFeature; 17 using extensions::ComplexFeature; 18 using extensions::DictionaryBuilder; 19 using extensions::Feature; 20 using extensions::ListBuilder; 21 using extensions::Manifest; 22 using extensions::ScopedCurrentChannel; 23 using extensions::SimpleFeature; 24 using extensions::test_util::ParseJsonDictionaryWithSingleQuotes; 25 26 namespace { 27 28 class ExtensionComplexFeatureTest : public testing::Test { 29 protected: 30 ExtensionComplexFeatureTest() 31 : current_channel_(VersionInfo::CHANNEL_UNKNOWN) {} 32 virtual ~ExtensionComplexFeatureTest() {} 33 34 SimpleFeature* CreateFeature() { 35 SimpleFeature* feature = new SimpleFeature(); 36 feature->AddFilter(scoped_ptr<extensions::SimpleFeatureFilter>( 37 new extensions::ChromeChannelFeatureFilter(feature))); 38 return feature; 39 } 40 41 private: 42 ScopedCurrentChannel current_channel_; 43 }; 44 45 TEST_F(ExtensionComplexFeatureTest, MultipleRulesWhitelist) { 46 const std::string kIdFoo("fooabbbbccccddddeeeeffffgggghhhh"); 47 const std::string kIdBar("barabbbbccccddddeeeeffffgggghhhh"); 48 scoped_ptr<ComplexFeature::FeatureList> features( 49 new ComplexFeature::FeatureList()); 50 51 // Rule: "extension", whitelist "foo". 52 scoped_ptr<SimpleFeature> simple_feature(CreateFeature()); 53 scoped_ptr<base::DictionaryValue> rule( 54 DictionaryBuilder() 55 .Set("whitelist", ListBuilder().Append(kIdFoo)) 56 .Set("extension_types", ListBuilder() 57 .Append("extension")).Build()); 58 simple_feature->Parse(rule.get()); 59 features->push_back(simple_feature.release()); 60 61 // Rule: "legacy_packaged_app", whitelist "bar". 62 simple_feature.reset(CreateFeature()); 63 rule = DictionaryBuilder() 64 .Set("whitelist", ListBuilder().Append(kIdBar)) 65 .Set("extension_types", ListBuilder() 66 .Append("legacy_packaged_app")).Build(); 67 simple_feature->Parse(rule.get()); 68 features->push_back(simple_feature.release()); 69 70 scoped_ptr<ComplexFeature> feature(new ComplexFeature(features.Pass())); 71 72 // Test match 1st rule. 73 EXPECT_EQ( 74 Feature::IS_AVAILABLE, 75 feature->IsAvailableToManifest(kIdFoo, 76 Manifest::TYPE_EXTENSION, 77 Manifest::INVALID_LOCATION, 78 Feature::UNSPECIFIED_PLATFORM, 79 Feature::GetCurrentPlatform()).result()); 80 81 // Test match 2nd rule. 82 EXPECT_EQ( 83 Feature::IS_AVAILABLE, 84 feature->IsAvailableToManifest(kIdBar, 85 Manifest::TYPE_LEGACY_PACKAGED_APP, 86 Manifest::INVALID_LOCATION, 87 Feature::UNSPECIFIED_PLATFORM, 88 Feature::GetCurrentPlatform()).result()); 89 90 // Test whitelist with wrong extension type. 91 EXPECT_NE( 92 Feature::IS_AVAILABLE, 93 feature->IsAvailableToManifest(kIdBar, 94 Manifest::TYPE_EXTENSION, 95 Manifest::INVALID_LOCATION, 96 Feature::UNSPECIFIED_PLATFORM, 97 Feature::GetCurrentPlatform()).result()); 98 EXPECT_NE( 99 Feature::IS_AVAILABLE, 100 feature->IsAvailableToManifest(kIdFoo, 101 Manifest::TYPE_LEGACY_PACKAGED_APP, 102 Manifest::INVALID_LOCATION, 103 Feature::UNSPECIFIED_PLATFORM, 104 Feature::GetCurrentPlatform()).result()); 105 } 106 107 TEST_F(ExtensionComplexFeatureTest, MultipleRulesChannels) { 108 scoped_ptr<ComplexFeature::FeatureList> features( 109 new ComplexFeature::FeatureList()); 110 111 // Rule: "extension", channel trunk. 112 scoped_ptr<SimpleFeature> simple_feature(CreateFeature()); 113 scoped_ptr<base::DictionaryValue> rule( 114 DictionaryBuilder() 115 .Set("channel", "trunk") 116 .Set("extension_types", ListBuilder().Append("extension")).Build()); 117 simple_feature->Parse(rule.get()); 118 features->push_back(simple_feature.release()); 119 120 // Rule: "legacy_packaged_app", channel stable. 121 simple_feature.reset(CreateFeature()); 122 rule = DictionaryBuilder() 123 .Set("channel", "stable") 124 .Set("extension_types", ListBuilder() 125 .Append("legacy_packaged_app")).Build(); 126 simple_feature->Parse(rule.get()); 127 features->push_back(simple_feature.release()); 128 129 scoped_ptr<ComplexFeature> feature(new ComplexFeature(features.Pass())); 130 131 // Test match 1st rule. 132 { 133 ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_UNKNOWN); 134 EXPECT_EQ( 135 Feature::IS_AVAILABLE, 136 feature->IsAvailableToManifest("1", 137 Manifest::TYPE_EXTENSION, 138 Manifest::INVALID_LOCATION, 139 Feature::UNSPECIFIED_PLATFORM, 140 Feature::GetCurrentPlatform()).result()); 141 } 142 143 // Test match 2nd rule. 144 { 145 ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_BETA); 146 EXPECT_EQ( 147 Feature::IS_AVAILABLE, 148 feature->IsAvailableToManifest("2", 149 Manifest::TYPE_LEGACY_PACKAGED_APP, 150 Manifest::INVALID_LOCATION, 151 Feature::UNSPECIFIED_PLATFORM, 152 Feature::GetCurrentPlatform()).result()); 153 } 154 155 // Test feature not available to extensions above channel unknown. 156 { 157 ScopedCurrentChannel current_channel(VersionInfo::CHANNEL_BETA); 158 EXPECT_NE( 159 Feature::IS_AVAILABLE, 160 feature->IsAvailableToManifest("1", 161 Manifest::TYPE_EXTENSION, 162 Manifest::INVALID_LOCATION, 163 Feature::UNSPECIFIED_PLATFORM, 164 Feature::GetCurrentPlatform()).result()); 165 } 166 } 167 168 // Tests a complex feature with blocked_in_service_worker returns true for 169 // IsBlockedInServiceWorker(). 170 TEST_F(ExtensionComplexFeatureTest, BlockedInServiceWorker) { 171 scoped_ptr<ComplexFeature::FeatureList> features( 172 new ComplexFeature::FeatureList()); 173 174 // Two feature rules, both with blocked_in_service_worker: true. 175 scoped_ptr<SimpleFeature> api_feature(new APIFeature()); 176 api_feature->Parse(ParseJsonDictionaryWithSingleQuotes( 177 "{" 178 " 'channel': 'trunk'," 179 " 'blocked_in_service_worker': true" 180 "}").get()); 181 features->push_back(api_feature.release()); 182 183 api_feature.reset(new APIFeature()); 184 api_feature->Parse(ParseJsonDictionaryWithSingleQuotes( 185 "{" 186 " 'channel': 'stable'," 187 " 'blocked_in_service_worker': true" 188 "}").get()); 189 features->push_back(api_feature.release()); 190 191 EXPECT_TRUE(ComplexFeature(features.Pass()).IsBlockedInServiceWorker()); 192 } 193 194 // Tests a complex feature without blocked_in_service_worker returns false for 195 // IsBlockedInServiceWorker(). 196 TEST_F(ExtensionComplexFeatureTest, NotBlockedInServiceWorker) { 197 scoped_ptr<ComplexFeature::FeatureList> features( 198 new ComplexFeature::FeatureList()); 199 200 // Two feature rules without blocked_in_service_worker. 201 scoped_ptr<SimpleFeature> api_feature(new APIFeature()); 202 api_feature->Parse(ParseJsonDictionaryWithSingleQuotes( 203 "{" 204 " 'channel': 'trunk'" 205 "}").get()); 206 features->push_back(api_feature.release()); 207 208 api_feature.reset(new APIFeature()); 209 api_feature->Parse(ParseJsonDictionaryWithSingleQuotes( 210 "{" 211 " 'channel': 'stable'" 212 "}").get()); 213 features->push_back(api_feature.release()); 214 215 EXPECT_FALSE(ComplexFeature(features.Pass()).IsBlockedInServiceWorker()); 216 } 217 218 } // namespace 219