OLD | NEW |
| (Empty) |
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/extension_api.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/files/file_path.h" | |
11 #include "base/files/file_util.h" | |
12 #include "base/json/json_reader.h" | |
13 #include "base/json/json_writer.h" | |
14 #include "base/memory/ref_counted.h" | |
15 #include "base/memory/scoped_ptr.h" | |
16 #include "base/path_service.h" | |
17 #include "base/strings/stringprintf.h" | |
18 #include "base/values.h" | |
19 #include "chrome/common/chrome_paths.h" | |
20 #include "extensions/common/extension.h" | |
21 #include "extensions/common/extension_builder.h" | |
22 #include "extensions/common/features/api_feature.h" | |
23 #include "extensions/common/features/base_feature_provider.h" | |
24 #include "extensions/common/features/simple_feature.h" | |
25 #include "extensions/common/manifest.h" | |
26 #include "extensions/common/manifest_constants.h" | |
27 #include "extensions/common/test_util.h" | |
28 #include "extensions/common/value_builder.h" | |
29 #include "testing/gtest/include/gtest/gtest.h" | |
30 | |
31 namespace extensions { | |
32 | |
33 using test_util::BuildExtension; | |
34 | |
35 SimpleFeature* CreateAPIFeature() { | |
36 return new APIFeature(); | |
37 } | |
38 | |
39 TEST(ExtensionAPITest, Creation) { | |
40 ExtensionAPI* shared_instance = ExtensionAPI::GetSharedInstance(); | |
41 EXPECT_EQ(shared_instance, ExtensionAPI::GetSharedInstance()); | |
42 | |
43 scoped_ptr<ExtensionAPI> new_instance( | |
44 ExtensionAPI::CreateWithDefaultConfiguration()); | |
45 EXPECT_NE(new_instance.get(), | |
46 scoped_ptr<ExtensionAPI>( | |
47 ExtensionAPI::CreateWithDefaultConfiguration()).get()); | |
48 | |
49 ExtensionAPI empty_instance; | |
50 | |
51 struct { | |
52 ExtensionAPI* api; | |
53 bool expect_populated; | |
54 } test_data[] = { | |
55 { shared_instance, true }, | |
56 { new_instance.get(), true }, | |
57 { &empty_instance, false } | |
58 }; | |
59 | |
60 for (size_t i = 0; i < arraysize(test_data); ++i) { | |
61 EXPECT_EQ(test_data[i].expect_populated, | |
62 test_data[i].api->GetSchema("bookmarks.create") != NULL); | |
63 } | |
64 } | |
65 | |
66 TEST(ExtensionAPITest, SplitDependencyName) { | |
67 struct { | |
68 std::string input; | |
69 std::string expected_feature_type; | |
70 std::string expected_feature_name; | |
71 } test_data[] = {{"", "api", ""}, // assumes "api" when no type is present | |
72 {"foo", "api", "foo"}, | |
73 {"foo:", "foo", ""}, | |
74 {":foo", "", "foo"}, | |
75 {"foo:bar", "foo", "bar"}, | |
76 {"foo:bar.baz", "foo", "bar.baz"}}; | |
77 | |
78 for (size_t i = 0; i < arraysize(test_data); ++i) { | |
79 std::string feature_type; | |
80 std::string feature_name; | |
81 ExtensionAPI::SplitDependencyName( | |
82 test_data[i].input, &feature_type, &feature_name); | |
83 EXPECT_EQ(test_data[i].expected_feature_type, feature_type) << i; | |
84 EXPECT_EQ(test_data[i].expected_feature_name, feature_name) << i; | |
85 } | |
86 } | |
87 | |
88 TEST(ExtensionAPITest, APIFeatures) { | |
89 struct { | |
90 std::string api_full_name; | |
91 bool expect_is_available; | |
92 Feature::Context context; | |
93 GURL url; | |
94 } test_data[] = { | |
95 { "test1", false, Feature::WEB_PAGE_CONTEXT, GURL() }, | |
96 { "test1", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, | |
97 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, | |
98 { "test1", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
99 { "test2", true, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") }, | |
100 { "test2", false, Feature::BLESSED_EXTENSION_CONTEXT, | |
101 GURL("http://google.com") }, | |
102 { "test2.foo", false, Feature::WEB_PAGE_CONTEXT, | |
103 GURL("http://google.com") }, | |
104 { "test2.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
105 { "test3", false, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") }, | |
106 { "test3.foo", true, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") }, | |
107 { "test3.foo", true, Feature::BLESSED_EXTENSION_CONTEXT, | |
108 GURL("http://bad.com") }, | |
109 { "test4", true, Feature::BLESSED_EXTENSION_CONTEXT, | |
110 GURL("http://bad.com") }, | |
111 { "test4.foo", false, Feature::BLESSED_EXTENSION_CONTEXT, | |
112 GURL("http://bad.com") }, | |
113 { "test4.foo", false, Feature::UNBLESSED_EXTENSION_CONTEXT, | |
114 GURL("http://bad.com") }, | |
115 { "test4.foo.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
116 { "test5", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, | |
117 { "test5", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, | |
118 { "test5.blah", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, | |
119 { "test5.blah", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, | |
120 { "test6", false, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, | |
121 { "test6.foo", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, | |
122 { "test7", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, | |
123 { "test7.foo", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, | |
124 { "test7.foo", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, | |
125 { "test7.bar", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, | |
126 { "test7.bar", false, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, | |
127 | |
128 // Test parent/child. | |
129 { "parent1", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
130 { "parent1", false, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, | |
131 { "parent1.child1", false, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
132 { "parent1.child1", true, Feature::WEB_PAGE_CONTEXT, | |
133 GURL("http://foo.com") }, | |
134 { "parent1.child2", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
135 { "parent1.child2", false, Feature::WEB_PAGE_CONTEXT, | |
136 GURL("http://foo.com") }, | |
137 { "parent2", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
138 { "parent2", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, | |
139 { "parent2", true, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, | |
140 { "parent2.child3", false, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
141 { "parent2.child3", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, | |
142 { "parent2.child3", false, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, | |
143 { "parent2.child3.child.child", true, Feature::CONTENT_SCRIPT_CONTEXT, | |
144 GURL() }, | |
145 { "parent2.child3.child.child", false, Feature::BLESSED_EXTENSION_CONTEXT, | |
146 GURL() }, | |
147 { "parent2.child3.child.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT, | |
148 GURL() }, | |
149 { "parent3", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
150 { "parent3", false, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, | |
151 { "parent3", false, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, | |
152 { "parent3.noparent", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
153 { "parent3.noparent", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, | |
154 { "parent3.noparent", true, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, | |
155 { "parent3.noparent.child", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, | |
156 { "parent3.noparent.child", true, Feature::BLESSED_EXTENSION_CONTEXT, | |
157 GURL() }, | |
158 { "parent3.noparent.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT, | |
159 GURL() } | |
160 }; | |
161 | |
162 base::FilePath api_features_path; | |
163 PathService::Get(chrome::DIR_TEST_DATA, &api_features_path); | |
164 api_features_path = api_features_path.AppendASCII("extensions") | |
165 .AppendASCII("extension_api_unittest") | |
166 .AppendASCII("api_features.json"); | |
167 | |
168 std::string api_features_str; | |
169 ASSERT_TRUE(base::ReadFileToString( | |
170 api_features_path, &api_features_str)) << "api_features.json"; | |
171 | |
172 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>( | |
173 base::JSONReader::DeprecatedRead(api_features_str))); | |
174 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature); | |
175 | |
176 for (size_t i = 0; i < arraysize(test_data); ++i) { | |
177 ExtensionAPI api; | |
178 api.RegisterDependencyProvider("api", &api_feature_provider); | |
179 for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd(); | |
180 iter.Advance()) { | |
181 if (iter.key().find(".") == std::string::npos) | |
182 api.RegisterSchemaResource(iter.key(), 0); | |
183 } | |
184 | |
185 ExtensionAPI::OverrideSharedInstanceForTest scope(&api); | |
186 bool expected = test_data[i].expect_is_available; | |
187 Feature::Availability availability = | |
188 api.IsAvailable(test_data[i].api_full_name, | |
189 NULL, | |
190 test_data[i].context, | |
191 test_data[i].url); | |
192 EXPECT_EQ(expected, availability.is_available()) | |
193 << base::StringPrintf("Test %d: Feature '%s' was %s: %s", | |
194 static_cast<int>(i), | |
195 test_data[i].api_full_name.c_str(), | |
196 expected ? "not available" : "available", | |
197 availability.message().c_str()); | |
198 } | |
199 } | |
200 | |
201 TEST(ExtensionAPITest, IsAnyFeatureAvailableToContext) { | |
202 scoped_refptr<const Extension> app = ExtensionBuilder() | |
203 .SetManifest(DictionaryBuilder() | |
204 .Set("name", "app") | |
205 .Set("app", DictionaryBuilder() | |
206 .Set("background", DictionaryBuilder() | |
207 .Set("scripts", ListBuilder().Append("background.js")))) | |
208 .Set("version", "1") | |
209 .Set("manifest_version", 2)).Build(); | |
210 scoped_refptr<const Extension> extension = ExtensionBuilder() | |
211 .SetManifest(DictionaryBuilder() | |
212 .Set("name", "extension") | |
213 .Set("version", "1") | |
214 .Set("manifest_version", 2)).Build(); | |
215 | |
216 struct { | |
217 std::string api_full_name; | |
218 bool expect_is_available; | |
219 Feature::Context context; | |
220 const Extension* extension; | |
221 GURL url; | |
222 } test_data[] = { | |
223 { "test1", false, Feature::WEB_PAGE_CONTEXT, NULL, GURL() }, | |
224 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, NULL, GURL() }, | |
225 { "test1", false, Feature::UNBLESSED_EXTENSION_CONTEXT, app.get(), GURL() }, | |
226 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, extension.get(), | |
227 GURL() }, | |
228 { "test2", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() }, | |
229 { "test2", true, Feature::WEB_PAGE_CONTEXT, NULL, | |
230 GURL("http://google.com") }, | |
231 { "test2.foo", false, Feature::WEB_PAGE_CONTEXT, NULL, | |
232 GURL("http://google.com") }, | |
233 { "test3", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() }, | |
234 { "test3", true, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://foo.com") }, | |
235 { "test4.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() }, | |
236 { "test7", false, Feature::WEB_PAGE_CONTEXT, NULL, | |
237 GURL("http://google.com") }, | |
238 { "test7", true, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://foo.com") }, | |
239 { "test7", false, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://bar.com") } | |
240 }; | |
241 | |
242 base::FilePath api_features_path; | |
243 PathService::Get(chrome::DIR_TEST_DATA, &api_features_path); | |
244 api_features_path = api_features_path.AppendASCII("extensions") | |
245 .AppendASCII("extension_api_unittest") | |
246 .AppendASCII("api_features.json"); | |
247 | |
248 std::string api_features_str; | |
249 ASSERT_TRUE(base::ReadFileToString( | |
250 api_features_path, &api_features_str)) << "api_features.json"; | |
251 | |
252 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>( | |
253 base::JSONReader::DeprecatedRead(api_features_str))); | |
254 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature); | |
255 | |
256 for (size_t i = 0; i < arraysize(test_data); ++i) { | |
257 ExtensionAPI api; | |
258 api.RegisterDependencyProvider("api", &api_feature_provider); | |
259 for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd(); | |
260 iter.Advance()) { | |
261 if (iter.key().find(".") == std::string::npos) | |
262 api.RegisterSchemaResource(iter.key(), 0); | |
263 } | |
264 | |
265 Feature* test_feature = | |
266 api_feature_provider.GetFeature(test_data[i].api_full_name); | |
267 ASSERT_TRUE(test_feature); | |
268 EXPECT_EQ(test_data[i].expect_is_available, | |
269 api.IsAnyFeatureAvailableToContext(*test_feature, | |
270 test_data[i].extension, | |
271 test_data[i].context, | |
272 test_data[i].url)) | |
273 << i; | |
274 } | |
275 } | |
276 | |
277 TEST(ExtensionAPITest, LazyGetSchema) { | |
278 scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration()); | |
279 | |
280 EXPECT_EQ(NULL, apis->GetSchema(std::string())); | |
281 EXPECT_EQ(NULL, apis->GetSchema(std::string())); | |
282 EXPECT_EQ(NULL, apis->GetSchema("experimental")); | |
283 EXPECT_EQ(NULL, apis->GetSchema("experimental")); | |
284 EXPECT_EQ(NULL, apis->GetSchema("foo")); | |
285 EXPECT_EQ(NULL, apis->GetSchema("foo")); | |
286 | |
287 EXPECT_TRUE(apis->GetSchema("dns")); | |
288 EXPECT_TRUE(apis->GetSchema("dns")); | |
289 EXPECT_TRUE(apis->GetSchema("extension")); | |
290 EXPECT_TRUE(apis->GetSchema("extension")); | |
291 EXPECT_TRUE(apis->GetSchema("omnibox")); | |
292 EXPECT_TRUE(apis->GetSchema("omnibox")); | |
293 EXPECT_TRUE(apis->GetSchema("storage")); | |
294 EXPECT_TRUE(apis->GetSchema("storage")); | |
295 } | |
296 | |
297 scoped_refptr<Extension> CreateExtensionWithPermissions( | |
298 const std::set<std::string>& permissions) { | |
299 base::DictionaryValue manifest; | |
300 manifest.SetString("name", "extension"); | |
301 manifest.SetString("version", "1.0"); | |
302 manifest.SetInteger("manifest_version", 2); | |
303 { | |
304 scoped_ptr<base::ListValue> permissions_list(new base::ListValue()); | |
305 for (std::set<std::string>::const_iterator i = permissions.begin(); | |
306 i != permissions.end(); ++i) { | |
307 permissions_list->Append(new base::StringValue(*i)); | |
308 } | |
309 manifest.Set("permissions", permissions_list.release()); | |
310 } | |
311 | |
312 std::string error; | |
313 scoped_refptr<Extension> extension(Extension::Create( | |
314 base::FilePath(), Manifest::UNPACKED, | |
315 manifest, Extension::NO_FLAGS, &error)); | |
316 CHECK(extension.get()); | |
317 CHECK(error.empty()); | |
318 | |
319 return extension; | |
320 } | |
321 | |
322 scoped_refptr<Extension> CreateExtensionWithPermission( | |
323 const std::string& permission) { | |
324 std::set<std::string> permissions; | |
325 permissions.insert(permission); | |
326 return CreateExtensionWithPermissions(permissions); | |
327 } | |
328 | |
329 TEST(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) { | |
330 scoped_refptr<Extension> extension; | |
331 { | |
332 std::set<std::string> permissions; | |
333 permissions.insert("storage"); | |
334 permissions.insert("history"); | |
335 extension = CreateExtensionWithPermissions(permissions); | |
336 } | |
337 | |
338 scoped_ptr<ExtensionAPI> extension_api( | |
339 ExtensionAPI::CreateWithDefaultConfiguration()); | |
340 | |
341 const FeatureProvider& api_features = *FeatureProvider::GetAPIFeatures(); | |
342 | |
343 // "storage" is completely unprivileged. | |
344 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( | |
345 *api_features.GetFeature("storage"), | |
346 NULL, | |
347 Feature::BLESSED_EXTENSION_CONTEXT, | |
348 GURL())); | |
349 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( | |
350 *api_features.GetFeature("storage"), | |
351 NULL, | |
352 Feature::UNBLESSED_EXTENSION_CONTEXT, | |
353 GURL())); | |
354 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( | |
355 *api_features.GetFeature("storage"), | |
356 NULL, | |
357 Feature::CONTENT_SCRIPT_CONTEXT, | |
358 GURL())); | |
359 | |
360 // "extension" is partially unprivileged. | |
361 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( | |
362 *api_features.GetFeature("extension"), | |
363 NULL, | |
364 Feature::BLESSED_EXTENSION_CONTEXT, | |
365 GURL())); | |
366 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( | |
367 *api_features.GetFeature("extension"), | |
368 NULL, | |
369 Feature::UNBLESSED_EXTENSION_CONTEXT, | |
370 GURL())); | |
371 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( | |
372 *api_features.GetFeature("extension"), | |
373 NULL, | |
374 Feature::CONTENT_SCRIPT_CONTEXT, | |
375 GURL())); | |
376 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( | |
377 *api_features.GetFeature("extension.getURL"), | |
378 NULL, | |
379 Feature::CONTENT_SCRIPT_CONTEXT, | |
380 GURL())); | |
381 | |
382 // "history" is entirely privileged. | |
383 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( | |
384 *api_features.GetFeature("history"), | |
385 NULL, | |
386 Feature::BLESSED_EXTENSION_CONTEXT, | |
387 GURL())); | |
388 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext( | |
389 *api_features.GetFeature("history"), | |
390 NULL, | |
391 Feature::UNBLESSED_EXTENSION_CONTEXT, | |
392 GURL())); | |
393 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext( | |
394 *api_features.GetFeature("history"), | |
395 NULL, | |
396 Feature::CONTENT_SCRIPT_CONTEXT, | |
397 GURL())); | |
398 } | |
399 | |
400 scoped_refptr<Extension> CreateHostedApp() { | |
401 base::DictionaryValue values; | |
402 values.SetString(manifest_keys::kName, "test"); | |
403 values.SetString(manifest_keys::kVersion, "0.1"); | |
404 values.Set(manifest_keys::kWebURLs, new base::ListValue()); | |
405 values.SetString(manifest_keys::kLaunchWebURL, | |
406 "http://www.example.com"); | |
407 std::string error; | |
408 scoped_refptr<Extension> extension(Extension::Create( | |
409 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS, | |
410 &error)); | |
411 CHECK(extension.get()); | |
412 return extension; | |
413 } | |
414 | |
415 scoped_refptr<Extension> CreatePackagedAppWithPermissions( | |
416 const std::set<std::string>& permissions) { | |
417 base::DictionaryValue values; | |
418 values.SetString(manifest_keys::kName, "test"); | |
419 values.SetString(manifest_keys::kVersion, "0.1"); | |
420 values.SetString(manifest_keys::kPlatformAppBackground, | |
421 "http://www.example.com"); | |
422 | |
423 base::DictionaryValue* app = new base::DictionaryValue(); | |
424 base::DictionaryValue* background = new base::DictionaryValue(); | |
425 base::ListValue* scripts = new base::ListValue(); | |
426 scripts->Append(new base::StringValue("test.js")); | |
427 background->Set("scripts", scripts); | |
428 app->Set("background", background); | |
429 values.Set(manifest_keys::kApp, app); | |
430 { | |
431 scoped_ptr<base::ListValue> permissions_list(new base::ListValue()); | |
432 for (std::set<std::string>::const_iterator i = permissions.begin(); | |
433 i != permissions.end(); ++i) { | |
434 permissions_list->Append(new base::StringValue(*i)); | |
435 } | |
436 values.Set("permissions", permissions_list.release()); | |
437 } | |
438 | |
439 std::string error; | |
440 scoped_refptr<Extension> extension(Extension::Create( | |
441 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS, | |
442 &error)); | |
443 CHECK(extension.get()) << error; | |
444 return extension; | |
445 } | |
446 | |
447 TEST(ExtensionAPITest, HostedAppPermissions) { | |
448 scoped_refptr<Extension> extension = CreateHostedApp(); | |
449 | |
450 scoped_ptr<ExtensionAPI> extension_api( | |
451 ExtensionAPI::CreateWithDefaultConfiguration()); | |
452 | |
453 // "runtime" and "tabs" should not be available in hosted apps. | |
454 EXPECT_FALSE(extension_api->IsAvailable("runtime", | |
455 extension.get(), | |
456 Feature::BLESSED_EXTENSION_CONTEXT, | |
457 GURL()).is_available()); | |
458 EXPECT_FALSE(extension_api->IsAvailable("runtime.id", | |
459 extension.get(), | |
460 Feature::BLESSED_EXTENSION_CONTEXT, | |
461 GURL()).is_available()); | |
462 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendMessage", | |
463 extension.get(), | |
464 Feature::BLESSED_EXTENSION_CONTEXT, | |
465 GURL()).is_available()); | |
466 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendNativeMessage", | |
467 extension.get(), | |
468 Feature::BLESSED_EXTENSION_CONTEXT, | |
469 GURL()).is_available()); | |
470 EXPECT_FALSE(extension_api->IsAvailable("tabs.create", | |
471 extension.get(), | |
472 Feature::BLESSED_EXTENSION_CONTEXT, | |
473 GURL()).is_available()); | |
474 } | |
475 | |
476 TEST(ExtensionAPITest, AppAndFriendsAvailability) { | |
477 | |
478 scoped_ptr<ExtensionAPI> extension_api( | |
479 ExtensionAPI::CreateWithDefaultConfiguration()); | |
480 | |
481 // Make sure chrome.app.runtime and chrome.app.window are available to apps, | |
482 // and chrome.app is not. | |
483 { | |
484 std::set<std::string> permissions; | |
485 permissions.insert("app.runtime"); | |
486 permissions.insert("app.window"); | |
487 scoped_refptr<Extension> extension = | |
488 CreatePackagedAppWithPermissions(permissions); | |
489 EXPECT_FALSE(extension_api->IsAvailable( | |
490 "app", | |
491 extension.get(), | |
492 Feature::BLESSED_EXTENSION_CONTEXT, | |
493 GURL("http://foo.com")).is_available()); | |
494 EXPECT_TRUE(extension_api->IsAvailable( | |
495 "app.runtime", | |
496 extension.get(), | |
497 Feature::BLESSED_EXTENSION_CONTEXT, | |
498 GURL("http://foo.com")).is_available()); | |
499 EXPECT_TRUE(extension_api->IsAvailable( | |
500 "app.window", | |
501 extension.get(), | |
502 Feature::BLESSED_EXTENSION_CONTEXT, | |
503 GURL("http://foo.com")).is_available()); | |
504 } | |
505 // Make sure chrome.app.runtime and chrome.app.window are not available to | |
506 // extensions, and chrome.app is. | |
507 { | |
508 std::set<std::string> permissions; | |
509 scoped_refptr<Extension> extension = | |
510 CreateExtensionWithPermissions(permissions); | |
511 EXPECT_TRUE(extension_api->IsAvailable( | |
512 "app", | |
513 extension.get(), | |
514 Feature::BLESSED_EXTENSION_CONTEXT, | |
515 GURL("http://foo.com")).is_available()); | |
516 EXPECT_FALSE(extension_api->IsAvailable( | |
517 "app.runtime", | |
518 extension.get(), | |
519 Feature::BLESSED_EXTENSION_CONTEXT, | |
520 GURL("http://foo.com")).is_available()); | |
521 EXPECT_FALSE(extension_api->IsAvailable( | |
522 "app.window", | |
523 extension.get(), | |
524 Feature::BLESSED_EXTENSION_CONTEXT, | |
525 GURL("http://foo.com")).is_available()); | |
526 } | |
527 } | |
528 | |
529 TEST(ExtensionAPITest, ExtensionWithDependencies) { | |
530 // Extension with the "ttsEngine" permission but not the "tts" permission; it | |
531 // should not automatically get "tts" permission. | |
532 { | |
533 scoped_refptr<Extension> extension = | |
534 CreateExtensionWithPermission("ttsEngine"); | |
535 scoped_ptr<ExtensionAPI> api( | |
536 ExtensionAPI::CreateWithDefaultConfiguration()); | |
537 EXPECT_TRUE(api->IsAvailable("ttsEngine", | |
538 extension.get(), | |
539 Feature::BLESSED_EXTENSION_CONTEXT, | |
540 GURL()).is_available()); | |
541 EXPECT_FALSE(api->IsAvailable("tts", | |
542 extension.get(), | |
543 Feature::BLESSED_EXTENSION_CONTEXT, | |
544 GURL()).is_available()); | |
545 } | |
546 | |
547 // Conversely, extension with the "tts" permission but not the "ttsEngine" | |
548 // permission shouldn't get the "ttsEngine" permission. | |
549 { | |
550 scoped_refptr<Extension> extension = | |
551 CreateExtensionWithPermission("tts"); | |
552 scoped_ptr<ExtensionAPI> api( | |
553 ExtensionAPI::CreateWithDefaultConfiguration()); | |
554 EXPECT_FALSE(api->IsAvailable("ttsEngine", | |
555 extension.get(), | |
556 Feature::BLESSED_EXTENSION_CONTEXT, | |
557 GURL()).is_available()); | |
558 EXPECT_TRUE(api->IsAvailable("tts", | |
559 extension.get(), | |
560 Feature::BLESSED_EXTENSION_CONTEXT, | |
561 GURL()).is_available()); | |
562 } | |
563 } | |
564 | |
565 bool MatchesURL( | |
566 ExtensionAPI* api, const std::string& api_name, const std::string& url) { | |
567 return api->IsAvailable( | |
568 api_name, NULL, Feature::WEB_PAGE_CONTEXT, GURL(url)).is_available(); | |
569 } | |
570 | |
571 TEST(ExtensionAPITest, URLMatching) { | |
572 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); | |
573 | |
574 // "app" API is available to all URLs that content scripts can be injected. | |
575 EXPECT_TRUE(MatchesURL(api.get(), "app", "http://example.com/example.html")); | |
576 EXPECT_TRUE(MatchesURL(api.get(), "app", "https://blah.net")); | |
577 EXPECT_TRUE(MatchesURL(api.get(), "app", "file://somefile.html")); | |
578 | |
579 // Also to internal URLs. | |
580 EXPECT_TRUE(MatchesURL(api.get(), "app", "about:flags")); | |
581 EXPECT_TRUE(MatchesURL(api.get(), "app", "chrome://flags")); | |
582 | |
583 // "app" should be available to chrome-extension URLs. | |
584 EXPECT_TRUE(MatchesURL(api.get(), "app", | |
585 "chrome-extension://fakeextension")); | |
586 | |
587 // "storage" API (for example) isn't available to any URLs. | |
588 EXPECT_FALSE(MatchesURL(api.get(), "storage", | |
589 "http://example.com/example.html")); | |
590 EXPECT_FALSE(MatchesURL(api.get(), "storage", "https://blah.net")); | |
591 EXPECT_FALSE(MatchesURL(api.get(), "storage", "file://somefile.html")); | |
592 EXPECT_FALSE(MatchesURL(api.get(), "storage", "about:flags")); | |
593 EXPECT_FALSE(MatchesURL(api.get(), "storage", "chrome://flags")); | |
594 EXPECT_FALSE(MatchesURL(api.get(), "storage", | |
595 "chrome-extension://fakeextension")); | |
596 } | |
597 | |
598 TEST(ExtensionAPITest, GetAPINameFromFullName) { | |
599 struct { | |
600 std::string input; | |
601 std::string api_name; | |
602 std::string child_name; | |
603 } test_data[] = { | |
604 { "", "", "" }, | |
605 { "unknown", "", "" }, | |
606 { "bookmarks", "bookmarks", "" }, | |
607 { "bookmarks.", "bookmarks", "" }, | |
608 { ".bookmarks", "", "" }, | |
609 { "bookmarks.create", "bookmarks", "create" }, | |
610 { "bookmarks.create.", "bookmarks", "create." }, | |
611 { "bookmarks.create.monkey", "bookmarks", "create.monkey" }, | |
612 { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" }, | |
613 { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" } | |
614 }; | |
615 | |
616 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); | |
617 for (size_t i = 0; i < arraysize(test_data); ++i) { | |
618 std::string child_name; | |
619 std::string api_name = api->GetAPINameFromFullName(test_data[i].input, | |
620 &child_name); | |
621 EXPECT_EQ(test_data[i].api_name, api_name) << test_data[i].input; | |
622 EXPECT_EQ(test_data[i].child_name, child_name) << test_data[i].input; | |
623 } | |
624 } | |
625 | |
626 TEST(ExtensionAPITest, DefaultConfigurationFeatures) { | |
627 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); | |
628 | |
629 SimpleFeature* bookmarks = static_cast<SimpleFeature*>( | |
630 api->GetFeatureDependency("api:bookmarks")); | |
631 SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>( | |
632 api->GetFeatureDependency("api:bookmarks.create")); | |
633 | |
634 struct { | |
635 SimpleFeature* feature; | |
636 // TODO(aa): More stuff to test over time. | |
637 } test_data[] = { | |
638 { bookmarks }, | |
639 { bookmarks_create } | |
640 }; | |
641 | |
642 for (size_t i = 0; i < arraysize(test_data); ++i) { | |
643 SimpleFeature* feature = test_data[i].feature; | |
644 ASSERT_TRUE(feature) << i; | |
645 | |
646 EXPECT_TRUE(feature->whitelist()->empty()); | |
647 EXPECT_TRUE(feature->extension_types()->empty()); | |
648 | |
649 EXPECT_EQ(SimpleFeature::UNSPECIFIED_LOCATION, feature->location()); | |
650 EXPECT_TRUE(feature->platforms()->empty()); | |
651 EXPECT_EQ(0, feature->min_manifest_version()); | |
652 EXPECT_EQ(0, feature->max_manifest_version()); | |
653 } | |
654 } | |
655 | |
656 TEST(ExtensionAPITest, FeaturesRequireContexts) { | |
657 // TODO(cduvall): Make this check API featues. | |
658 scoped_ptr<base::DictionaryValue> api_features1(new base::DictionaryValue()); | |
659 scoped_ptr<base::DictionaryValue> api_features2(new base::DictionaryValue()); | |
660 base::DictionaryValue* test1 = new base::DictionaryValue(); | |
661 base::DictionaryValue* test2 = new base::DictionaryValue(); | |
662 base::ListValue* contexts = new base::ListValue(); | |
663 contexts->Append(new base::StringValue("content_script")); | |
664 test1->Set("contexts", contexts); | |
665 test1->SetString("channel", "stable"); | |
666 test2->SetString("channel", "stable"); | |
667 api_features1->Set("test", test1); | |
668 api_features2->Set("test", test2); | |
669 | |
670 struct { | |
671 base::DictionaryValue* api_features; | |
672 bool expect_success; | |
673 } test_data[] = { | |
674 { api_features1.get(), true }, | |
675 { api_features2.get(), false } | |
676 }; | |
677 | |
678 for (size_t i = 0; i < arraysize(test_data); ++i) { | |
679 BaseFeatureProvider api_feature_provider(*test_data[i].api_features, | |
680 CreateAPIFeature); | |
681 Feature* feature = api_feature_provider.GetFeature("test"); | |
682 EXPECT_EQ(test_data[i].expect_success, feature != NULL) << i; | |
683 } | |
684 } | |
685 | |
686 static void GetDictionaryFromList(const base::DictionaryValue* schema, | |
687 const std::string& list_name, | |
688 const int list_index, | |
689 const base::DictionaryValue** out) { | |
690 const base::ListValue* list; | |
691 EXPECT_TRUE(schema->GetList(list_name, &list)); | |
692 EXPECT_TRUE(list->GetDictionary(list_index, out)); | |
693 } | |
694 | |
695 TEST(ExtensionAPITest, TypesHaveNamespace) { | |
696 base::FilePath manifest_path; | |
697 PathService::Get(chrome::DIR_TEST_DATA, &manifest_path); | |
698 manifest_path = manifest_path.AppendASCII("extensions") | |
699 .AppendASCII("extension_api_unittest") | |
700 .AppendASCII("types_have_namespace.json"); | |
701 | |
702 std::string manifest_str; | |
703 ASSERT_TRUE(base::ReadFileToString(manifest_path, &manifest_str)) | |
704 << "Failed to load: " << manifest_path.value(); | |
705 | |
706 ExtensionAPI api; | |
707 api.RegisterSchemaResource("test.foo", 0); | |
708 api.LoadSchema("test.foo", manifest_str); | |
709 | |
710 const base::DictionaryValue* schema = api.GetSchema("test.foo"); | |
711 | |
712 const base::DictionaryValue* dict; | |
713 const base::DictionaryValue* sub_dict; | |
714 std::string type; | |
715 | |
716 GetDictionaryFromList(schema, "types", 0, &dict); | |
717 EXPECT_TRUE(dict->GetString("id", &type)); | |
718 EXPECT_EQ("test.foo.TestType", type); | |
719 EXPECT_TRUE(dict->GetString("customBindings", &type)); | |
720 EXPECT_EQ("test.foo.TestType", type); | |
721 EXPECT_TRUE(dict->GetDictionary("properties", &sub_dict)); | |
722 const base::DictionaryValue* property; | |
723 EXPECT_TRUE(sub_dict->GetDictionary("foo", &property)); | |
724 EXPECT_TRUE(property->GetString("$ref", &type)); | |
725 EXPECT_EQ("test.foo.OtherType", type); | |
726 EXPECT_TRUE(sub_dict->GetDictionary("bar", &property)); | |
727 EXPECT_TRUE(property->GetString("$ref", &type)); | |
728 EXPECT_EQ("fully.qualified.Type", type); | |
729 | |
730 GetDictionaryFromList(schema, "functions", 0, &dict); | |
731 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); | |
732 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); | |
733 EXPECT_EQ("test.foo.TestType", type); | |
734 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict)); | |
735 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); | |
736 EXPECT_EQ("fully.qualified.Type", type); | |
737 | |
738 GetDictionaryFromList(schema, "functions", 1, &dict); | |
739 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); | |
740 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); | |
741 EXPECT_EQ("fully.qualified.Type", type); | |
742 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict)); | |
743 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); | |
744 EXPECT_EQ("test.foo.TestType", type); | |
745 | |
746 GetDictionaryFromList(schema, "events", 0, &dict); | |
747 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); | |
748 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); | |
749 EXPECT_EQ("test.foo.TestType", type); | |
750 GetDictionaryFromList(dict, "parameters", 1, &sub_dict); | |
751 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); | |
752 EXPECT_EQ("fully.qualified.Type", type); | |
753 } | |
754 | |
755 // Tests API availability with an empty manifest. | |
756 TEST(ExtensionAPITest, NoPermissions) { | |
757 const struct { | |
758 const char* permission_name; | |
759 bool expect_success; | |
760 } kTests[] = { | |
761 // Test default module/package permission. | |
762 { "extension", true }, | |
763 { "i18n", true }, | |
764 { "permissions", true }, | |
765 { "runtime", true }, | |
766 { "test", true }, | |
767 // These require manifest keys. | |
768 { "browserAction", false }, | |
769 { "pageAction", false }, | |
770 { "pageActions", false }, | |
771 // Some negative tests. | |
772 { "bookmarks", false }, | |
773 { "cookies", false }, | |
774 { "history", false }, | |
775 // Make sure we find the module name after stripping '.' | |
776 { "runtime.abcd.onStartup", true }, | |
777 // Test Tabs/Windows functions. | |
778 { "tabs.create", true }, | |
779 { "tabs.duplicate", true }, | |
780 { "tabs.onRemoved", true }, | |
781 { "tabs.remove", true }, | |
782 { "tabs.update", true }, | |
783 { "tabs.getSelected", true }, | |
784 { "tabs.onUpdated", true }, | |
785 { "windows.get", true }, | |
786 { "windows.create", true }, | |
787 { "windows.remove", true }, | |
788 { "windows.update", true }, | |
789 // Test some whitelisted functions. These require no permissions. | |
790 { "app.getDetails", true }, | |
791 { "app.getDetailsForFrame", true }, | |
792 { "app.getIsInstalled", true }, | |
793 { "app.installState", true }, | |
794 { "app.runningState", true }, | |
795 { "management.getPermissionWarningsByManifest", true }, | |
796 { "management.uninstallSelf", true }, | |
797 // But other functions in those modules do. | |
798 { "management.getPermissionWarningsById", false }, | |
799 { "runtime.connectNative", false }, | |
800 }; | |
801 | |
802 scoped_ptr<ExtensionAPI> extension_api( | |
803 ExtensionAPI::CreateWithDefaultConfiguration()); | |
804 scoped_refptr<Extension> extension = | |
805 BuildExtension(ExtensionBuilder().Pass()).Build(); | |
806 | |
807 for (size_t i = 0; i < arraysize(kTests); ++i) { | |
808 EXPECT_EQ(kTests[i].expect_success, | |
809 extension_api->IsAvailable(kTests[i].permission_name, | |
810 extension.get(), | |
811 Feature::BLESSED_EXTENSION_CONTEXT, | |
812 GURL()).is_available()) | |
813 << "Permission being tested: " << kTests[i].permission_name; | |
814 } | |
815 } | |
816 | |
817 // Tests that permissions that require manifest keys are available when those | |
818 // keys are present. | |
819 TEST(ExtensionAPITest, ManifestKeys) { | |
820 scoped_ptr<ExtensionAPI> extension_api( | |
821 ExtensionAPI::CreateWithDefaultConfiguration()); | |
822 | |
823 scoped_refptr<Extension> extension = | |
824 BuildExtension(ExtensionBuilder().Pass()) | |
825 .MergeManifest(DictionaryBuilder().Set("browser_action", | |
826 DictionaryBuilder().Pass())) | |
827 .Build(); | |
828 | |
829 EXPECT_TRUE(extension_api->IsAvailable("browserAction", | |
830 extension.get(), | |
831 Feature::BLESSED_EXTENSION_CONTEXT, | |
832 GURL()).is_available()); | |
833 EXPECT_FALSE(extension_api->IsAvailable("pageAction", | |
834 extension.get(), | |
835 Feature::BLESSED_EXTENSION_CONTEXT, | |
836 GURL()).is_available()); | |
837 } | |
838 | |
839 } // namespace extensions | |
OLD | NEW |