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