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