// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/json/json_file_value_serializer.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_test_util.h"
#include "chrome/common/extensions/features/feature_channel.h"
#include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
#include "chrome/grit/generated_resources.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/permissions/permission_message_provider.h"
#include "extensions/common/permissions/permission_message_util.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/permissions/permissions_info.h"
#include "extensions/common/permissions/socket_permission.h"
#include "extensions/common/value_builder.h"
#include "extensions/strings/grit/extensions_strings.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"

using extension_test_util::LoadManifest;

namespace extensions {

namespace {

static void AddPattern(URLPatternSet* extent, const std::string& pattern) {
  int schemes = URLPattern::SCHEME_ALL;
  extent->AddPattern(URLPattern(schemes, pattern));
}

size_t IndexOf(const std::vector<base::string16>& warnings,
               const std::string& warning) {
  for (size_t i = 0; i < warnings.size(); ++i) {
    if (warnings[i] == base::ASCIIToUTF16(warning))
      return i;
  }

  return warnings.size();
}

bool Contains(const std::vector<base::string16>& warnings,
              const std::string& warning) {
  return IndexOf(warnings, warning) != warnings.size();
}

}  // namespace

// Tests GetByID.
TEST(PermissionsTest, GetByID) {
  PermissionsInfo* info = PermissionsInfo::GetInstance();
  APIPermissionSet apis = info->GetAll();
  for (APIPermissionSet::const_iterator i = apis.begin();
       i != apis.end(); ++i) {
    EXPECT_EQ(i->id(), i->info()->id());
  }
}

// Tests that GetByName works with normal permission names and aliases.
TEST(PermissionsTest, GetByName) {
  PermissionsInfo* info = PermissionsInfo::GetInstance();
  EXPECT_EQ(APIPermission::kTab, info->GetByName("tabs")->id());
  EXPECT_EQ(APIPermission::kManagement,
            info->GetByName("management")->id());
  EXPECT_FALSE(info->GetByName("alsdkfjasldkfj"));
}

TEST(PermissionsTest, GetAll) {
  size_t count = 0;
  PermissionsInfo* info = PermissionsInfo::GetInstance();
  APIPermissionSet apis = info->GetAll();
  for (APIPermissionSet::const_iterator api = apis.begin();
       api != apis.end(); ++api) {
    // Make sure only the valid permission IDs get returned.
    EXPECT_NE(APIPermission::kInvalid, api->id());
    EXPECT_NE(APIPermission::kUnknown, api->id());
    count++;
  }
  EXPECT_EQ(count, info->get_permission_count());
}

TEST(PermissionsTest, GetAllByName) {
  std::set<std::string> names;
  names.insert("background");
  names.insert("management");

  // This is an alias of kTab
  names.insert("windows");

  // This unknown name should get dropped.
  names.insert("sdlkfjasdlkfj");

  APIPermissionSet expected;
  expected.insert(APIPermission::kBackground);
  expected.insert(APIPermission::kManagement);
  expected.insert(APIPermission::kTab);

  EXPECT_EQ(expected,
            PermissionsInfo::GetInstance()->GetAllByName(names));
}

// Tests that the aliases are properly mapped.
TEST(PermissionsTest, Aliases) {
  PermissionsInfo* info = PermissionsInfo::GetInstance();
  // tabs: tabs, windows
  std::string tabs_name = "tabs";
  EXPECT_EQ(tabs_name, info->GetByID(APIPermission::kTab)->name());
  EXPECT_EQ(APIPermission::kTab, info->GetByName("tabs")->id());
  EXPECT_EQ(APIPermission::kTab, info->GetByName("windows")->id());

  // unlimitedStorage: unlimitedStorage, unlimited_storage
  std::string storage_name = "unlimitedStorage";
  EXPECT_EQ(storage_name, info->GetByID(
      APIPermission::kUnlimitedStorage)->name());
  EXPECT_EQ(APIPermission::kUnlimitedStorage,
            info->GetByName("unlimitedStorage")->id());
  EXPECT_EQ(APIPermission::kUnlimitedStorage,
            info->GetByName("unlimited_storage")->id());
}

TEST(PermissionsTest, EffectiveHostPermissions) {
  scoped_refptr<Extension> extension;
  scoped_refptr<const PermissionSet> permissions;

  extension = LoadManifest("effective_host_permissions", "empty.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_EQ(0u,
            extension->permissions_data()
                ->GetEffectiveHostPermissions()
                .patterns()
                .size());
  EXPECT_FALSE(
      permissions->HasEffectiveAccessToURL(GURL("http://www.google.com")));
  EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());

  extension = LoadManifest("effective_host_permissions", "one_host.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
      GURL("http://www.google.com")));
  EXPECT_FALSE(permissions->HasEffectiveAccessToURL(
      GURL("https://www.google.com")));
  EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());

  extension = LoadManifest("effective_host_permissions",
                           "one_host_wildcard.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
      GURL("http://foo.google.com")));
  EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());

  extension = LoadManifest("effective_host_permissions", "two_hosts.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
      GURL("http://www.google.com")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
      GURL("http://www.reddit.com")));
  EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());

  extension = LoadManifest("effective_host_permissions",
                           "https_not_considered.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://google.com")));
  EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());

  extension = LoadManifest("effective_host_permissions",
                           "two_content_scripts.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
      GURL("http://www.reddit.com")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
      GURL("http://news.ycombinator.com")));
  EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());

  extension = LoadManifest("effective_host_permissions", "all_hosts.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
  EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("https://test/")));
  EXPECT_TRUE(
      permissions->HasEffectiveAccessToURL(GURL("http://www.google.com")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts());

  extension = LoadManifest("effective_host_permissions", "all_hosts2.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
  EXPECT_TRUE(
      permissions->HasEffectiveAccessToURL(GURL("http://www.google.com")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts());

  extension = LoadManifest("effective_host_permissions", "all_hosts3.json");
  permissions = extension->permissions_data()->active_permissions();
  EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://test/")));
  EXPECT_TRUE(
      permissions->HasEffectiveAccessToURL(GURL("http://www.google.com")));
  EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts());
}

TEST(PermissionsTest, ExplicitAccessToOrigin) {
  APIPermissionSet apis;
  ManifestPermissionSet manifest_permissions;
  URLPatternSet explicit_hosts;
  URLPatternSet scriptable_hosts;

  AddPattern(&explicit_hosts, "http://*.google.com/*");
  // The explicit host paths should get set to /*.
  AddPattern(&explicit_hosts, "http://www.example.com/a/particular/path/*");

  scoped_refptr<PermissionSet> perm_set = new PermissionSet(
      apis, manifest_permissions, explicit_hosts, scriptable_hosts);
  ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin(
      GURL("http://www.google.com/")));
  ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin(
      GURL("http://test.google.com/")));
  ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin(
      GURL("http://www.example.com")));
  ASSERT_TRUE(perm_set->HasEffectiveAccessToURL(
      GURL("http://www.example.com")));
  ASSERT_FALSE(perm_set->HasExplicitAccessToOrigin(
      GURL("http://test.example.com")));
}

TEST(PermissionsTest, CreateUnion) {
  APIPermission* permission = NULL;

  ManifestPermissionSet manifest_permissions;
  APIPermissionSet apis1;
  APIPermissionSet apis2;
  APIPermissionSet expected_apis;

  URLPatternSet explicit_hosts1;
  URLPatternSet explicit_hosts2;
  URLPatternSet expected_explicit_hosts;

  URLPatternSet scriptable_hosts1;
  URLPatternSet scriptable_hosts2;
  URLPatternSet expected_scriptable_hosts;

  URLPatternSet effective_hosts;

  scoped_refptr<PermissionSet> set1;
  scoped_refptr<PermissionSet> set2;
  scoped_refptr<PermissionSet> union_set;

  const APIPermissionInfo* permission_info =
    PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket);
  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("tcp-connect:*.example.com:80"));
    value->Append(new base::StringValue("udp-bind::8080"));
    value->Append(new base::StringValue("udp-send-to::8888"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }

  // Union with an empty set.
  apis1.insert(APIPermission::kTab);
  apis1.insert(APIPermission::kBackground);
  apis1.insert(permission->Clone());
  expected_apis.insert(APIPermission::kTab);
  expected_apis.insert(APIPermission::kBackground);
  expected_apis.insert(permission);

  AddPattern(&explicit_hosts1, "http://*.google.com/*");
  AddPattern(&expected_explicit_hosts, "http://*.google.com/*");
  AddPattern(&effective_hosts, "http://*.google.com/*");

  set1 = new PermissionSet(apis1, manifest_permissions,
                           explicit_hosts1, scriptable_hosts1);
  set2 = new PermissionSet(apis2, manifest_permissions,
                           explicit_hosts2, scriptable_hosts2);
  union_set = PermissionSet::CreateUnion(set1.get(), set2.get());
  EXPECT_TRUE(set1->Contains(*set2.get()));
  EXPECT_TRUE(set1->Contains(*union_set.get()));
  EXPECT_FALSE(set2->Contains(*set1.get()));
  EXPECT_FALSE(set2->Contains(*union_set.get()));
  EXPECT_TRUE(union_set->Contains(*set1.get()));
  EXPECT_TRUE(union_set->Contains(*set2.get()));

  EXPECT_FALSE(union_set->HasEffectiveFullAccess());
  EXPECT_EQ(expected_apis, union_set->apis());
  EXPECT_EQ(expected_explicit_hosts, union_set->explicit_hosts());
  EXPECT_EQ(expected_scriptable_hosts, union_set->scriptable_hosts());
  EXPECT_EQ(expected_explicit_hosts, union_set->effective_hosts());

  // Now use a real second set.
  apis2.insert(APIPermission::kTab);
  apis2.insert(APIPermission::kProxy);
  apis2.insert(APIPermission::kClipboardWrite);
  apis2.insert(APIPermission::kPlugin);

  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("tcp-connect:*.example.com:80"));
    value->Append(new base::StringValue("udp-send-to::8899"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }
  apis2.insert(permission);

  expected_apis.insert(APIPermission::kTab);
  expected_apis.insert(APIPermission::kProxy);
  expected_apis.insert(APIPermission::kClipboardWrite);
  expected_apis.insert(APIPermission::kPlugin);

  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("tcp-connect:*.example.com:80"));
    value->Append(new base::StringValue("udp-bind::8080"));
    value->Append(new base::StringValue("udp-send-to::8888"));
    value->Append(new base::StringValue("udp-send-to::8899"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }
  // Insert a new permission socket permisssion which will replace the old one.
  expected_apis.insert(permission);

  AddPattern(&explicit_hosts2, "http://*.example.com/*");
  AddPattern(&scriptable_hosts2, "http://*.google.com/*");
  AddPattern(&expected_explicit_hosts, "http://*.example.com/*");
  AddPattern(&expected_scriptable_hosts, "http://*.google.com/*");

  URLPatternSet::CreateUnion(
      explicit_hosts2, scriptable_hosts2, &effective_hosts);

  set2 = new PermissionSet(apis2, manifest_permissions,
                           explicit_hosts2, scriptable_hosts2);
  union_set = PermissionSet::CreateUnion(set1.get(), set2.get());

  EXPECT_FALSE(set1->Contains(*set2.get()));
  EXPECT_FALSE(set1->Contains(*union_set.get()));
  EXPECT_FALSE(set2->Contains(*set1.get()));
  EXPECT_FALSE(set2->Contains(*union_set.get()));
  EXPECT_TRUE(union_set->Contains(*set1.get()));
  EXPECT_TRUE(union_set->Contains(*set2.get()));

  EXPECT_TRUE(union_set->HasEffectiveFullAccess());
  EXPECT_TRUE(union_set->HasEffectiveAccessToAllHosts());
  EXPECT_EQ(expected_apis, union_set->apis());
  EXPECT_EQ(expected_explicit_hosts, union_set->explicit_hosts());
  EXPECT_EQ(expected_scriptable_hosts, union_set->scriptable_hosts());
  EXPECT_EQ(effective_hosts, union_set->effective_hosts());
}

TEST(PermissionsTest, CreateIntersection) {
  APIPermission* permission = NULL;

  ManifestPermissionSet manifest_permissions;
  APIPermissionSet apis1;
  APIPermissionSet apis2;
  APIPermissionSet expected_apis;

  URLPatternSet explicit_hosts1;
  URLPatternSet explicit_hosts2;
  URLPatternSet expected_explicit_hosts;

  URLPatternSet scriptable_hosts1;
  URLPatternSet scriptable_hosts2;
  URLPatternSet expected_scriptable_hosts;

  URLPatternSet effective_hosts;

  scoped_refptr<PermissionSet> set1;
  scoped_refptr<PermissionSet> set2;
  scoped_refptr<PermissionSet> new_set;

  const APIPermissionInfo* permission_info =
    PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket);

  // Intersection with an empty set.
  apis1.insert(APIPermission::kTab);
  apis1.insert(APIPermission::kBackground);
  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("tcp-connect:*.example.com:80"));
    value->Append(new base::StringValue("udp-bind::8080"));
    value->Append(new base::StringValue("udp-send-to::8888"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }
  apis1.insert(permission);

  AddPattern(&explicit_hosts1, "http://*.google.com/*");
  AddPattern(&scriptable_hosts1, "http://www.reddit.com/*");

  set1 = new PermissionSet(apis1, manifest_permissions,
                           explicit_hosts1, scriptable_hosts1);
  set2 = new PermissionSet(apis2, manifest_permissions,
                           explicit_hosts2, scriptable_hosts2);
  new_set = PermissionSet::CreateIntersection(set1.get(), set2.get());
  EXPECT_TRUE(set1->Contains(*new_set.get()));
  EXPECT_TRUE(set2->Contains(*new_set.get()));
  EXPECT_TRUE(set1->Contains(*set2.get()));
  EXPECT_FALSE(set2->Contains(*set1.get()));
  EXPECT_FALSE(new_set->Contains(*set1.get()));
  EXPECT_TRUE(new_set->Contains(*set2.get()));

  EXPECT_TRUE(new_set->IsEmpty());
  EXPECT_FALSE(new_set->HasEffectiveFullAccess());
  EXPECT_EQ(expected_apis, new_set->apis());
  EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts());
  EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts());
  EXPECT_EQ(expected_explicit_hosts, new_set->effective_hosts());

  // Now use a real second set.
  apis2.insert(APIPermission::kTab);
  apis2.insert(APIPermission::kProxy);
  apis2.insert(APIPermission::kClipboardWrite);
  apis2.insert(APIPermission::kPlugin);
  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("udp-bind::8080"));
    value->Append(new base::StringValue("udp-send-to::8888"));
    value->Append(new base::StringValue("udp-send-to::8899"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }
  apis2.insert(permission);

  expected_apis.insert(APIPermission::kTab);
  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("udp-bind::8080"));
    value->Append(new base::StringValue("udp-send-to::8888"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }
  expected_apis.insert(permission);

  AddPattern(&explicit_hosts2, "http://*.example.com/*");
  AddPattern(&explicit_hosts2, "http://*.google.com/*");
  AddPattern(&scriptable_hosts2, "http://*.google.com/*");
  AddPattern(&expected_explicit_hosts, "http://*.google.com/*");

  effective_hosts.ClearPatterns();
  AddPattern(&effective_hosts, "http://*.google.com/*");

  set2 = new PermissionSet(apis2, manifest_permissions,
                           explicit_hosts2, scriptable_hosts2);
  new_set = PermissionSet::CreateIntersection(set1.get(), set2.get());

  EXPECT_TRUE(set1->Contains(*new_set.get()));
  EXPECT_TRUE(set2->Contains(*new_set.get()));
  EXPECT_FALSE(set1->Contains(*set2.get()));
  EXPECT_FALSE(set2->Contains(*set1.get()));
  EXPECT_FALSE(new_set->Contains(*set1.get()));
  EXPECT_FALSE(new_set->Contains(*set2.get()));

  EXPECT_FALSE(new_set->HasEffectiveFullAccess());
  EXPECT_FALSE(new_set->HasEffectiveAccessToAllHosts());
  EXPECT_EQ(expected_apis, new_set->apis());
  EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts());
  EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts());
  EXPECT_EQ(effective_hosts, new_set->effective_hosts());
}

TEST(PermissionsTest, CreateDifference) {
  APIPermission* permission = NULL;

  ManifestPermissionSet manifest_permissions;
  APIPermissionSet apis1;
  APIPermissionSet apis2;
  APIPermissionSet expected_apis;

  URLPatternSet explicit_hosts1;
  URLPatternSet explicit_hosts2;
  URLPatternSet expected_explicit_hosts;

  URLPatternSet scriptable_hosts1;
  URLPatternSet scriptable_hosts2;
  URLPatternSet expected_scriptable_hosts;

  URLPatternSet effective_hosts;

  scoped_refptr<PermissionSet> set1;
  scoped_refptr<PermissionSet> set2;
  scoped_refptr<PermissionSet> new_set;

  const APIPermissionInfo* permission_info =
    PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket);

  // Difference with an empty set.
  apis1.insert(APIPermission::kTab);
  apis1.insert(APIPermission::kBackground);
  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("tcp-connect:*.example.com:80"));
    value->Append(new base::StringValue("udp-bind::8080"));
    value->Append(new base::StringValue("udp-send-to::8888"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }
  apis1.insert(permission);

  AddPattern(&explicit_hosts1, "http://*.google.com/*");
  AddPattern(&scriptable_hosts1, "http://www.reddit.com/*");

  set1 = new PermissionSet(apis1, manifest_permissions,
                           explicit_hosts1, scriptable_hosts1);
  set2 = new PermissionSet(apis2, manifest_permissions,
                           explicit_hosts2, scriptable_hosts2);
  new_set = PermissionSet::CreateDifference(set1.get(), set2.get());
  EXPECT_EQ(*set1.get(), *new_set.get());

  // Now use a real second set.
  apis2.insert(APIPermission::kTab);
  apis2.insert(APIPermission::kProxy);
  apis2.insert(APIPermission::kClipboardWrite);
  apis2.insert(APIPermission::kPlugin);
  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("tcp-connect:*.example.com:80"));
    value->Append(new base::StringValue("udp-send-to::8899"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }
  apis2.insert(permission);

  expected_apis.insert(APIPermission::kBackground);
  permission = permission_info->CreateAPIPermission();
  {
    scoped_ptr<base::ListValue> value(new base::ListValue());
    value->Append(new base::StringValue("udp-bind::8080"));
    value->Append(new base::StringValue("udp-send-to::8888"));
    ASSERT_TRUE(permission->FromValue(value.get(), NULL, NULL));
  }
  expected_apis.insert(permission);

  AddPattern(&explicit_hosts2, "http://*.example.com/*");
  AddPattern(&explicit_hosts2, "http://*.google.com/*");
  AddPattern(&scriptable_hosts2, "http://*.google.com/*");
  AddPattern(&expected_scriptable_hosts, "http://www.reddit.com/*");

  effective_hosts.ClearPatterns();
  AddPattern(&effective_hosts, "http://www.reddit.com/*");

  set2 = new PermissionSet(apis2, manifest_permissions,
                           explicit_hosts2, scriptable_hosts2);
  new_set = PermissionSet::CreateDifference(set1.get(), set2.get());

  EXPECT_TRUE(set1->Contains(*new_set.get()));
  EXPECT_FALSE(set2->Contains(*new_set.get()));

  EXPECT_FALSE(new_set->HasEffectiveFullAccess());
  EXPECT_FALSE(new_set->HasEffectiveAccessToAllHosts());
  EXPECT_EQ(expected_apis, new_set->apis());
  EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts());
  EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts());
  EXPECT_EQ(effective_hosts, new_set->effective_hosts());

  // |set3| = |set1| - |set2| --> |set3| intersect |set2| == empty_set
  set1 = PermissionSet::CreateIntersection(new_set.get(), set2.get());
  EXPECT_TRUE(set1->IsEmpty());
}

TEST(PermissionsTest, IsPrivilegeIncrease) {
  const struct {
    const char* base_name;
    bool expect_increase;
  } kTests[] = {
    { "allhosts1", false },  // all -> all
    { "allhosts2", false },  // all -> one
    { "allhosts3", true },  // one -> all
    { "hosts1", false },  // http://a,http://b -> http://a,http://b
    { "hosts2", true },  // http://a,http://b -> https://a,http://*.b
    { "hosts3", false },  // http://a,http://b -> http://a
    { "hosts4", true },  // http://a -> http://a,http://b
    { "hosts5", false },  // http://a,b,c -> http://a,b,c + https://a,b,c
    { "hosts6", false },  // http://a.com -> http://a.com + http://a.co.uk
    { "permissions1", false },  // tabs -> tabs
    { "permissions2", true },  // tabs -> tabs,bookmarks
    { "permissions3", true },  // http://a -> http://a,tabs
    { "permissions5", true },  // bookmarks -> bookmarks,history
    { "equivalent_warnings", false },  // tabs --> tabs, webNavigation
#if !defined(OS_CHROMEOS)  // plugins aren't allowed in ChromeOS
    { "permissions4", false },  // plugin -> plugin,tabs
    { "plugin1", false },  // plugin -> plugin
    { "plugin2", false },  // plugin -> none
    { "plugin3", true },  // none -> plugin
#endif
    { "storage", false },  // none -> storage
    { "notifications", false },  // none -> notifications
    { "platformapp1", false },  // host permissions for platform apps
    { "platformapp2", true },  // API permissions for platform apps
    { "media_galleries1", true },  // all -> read|all
    { "media_galleries2", true },  // read|all -> read|delete|copyTo|all
    { "media_galleries3", true },  // all -> read|delete|all
    { "media_galleries4", false },  // read|all -> all
    { "media_galleries5", false },  // read|copyTo|delete|all -> read|all
    { "media_galleries6", false },  // read|all -> read|all
    { "media_galleries7", true },  // read|delete|all -> read|copyTo|delete|all
    { "sockets1", true },  // none -> tcp:*:*
    { "sockets2", false },  // tcp:*:* -> tcp:*:*
    { "sockets3", true },  // tcp:a.com:80 -> tcp:*:*
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
    scoped_refptr<Extension> old_extension(
        LoadManifest("allow_silent_upgrade",
                     std::string(kTests[i].base_name) + "_old.json"));
    scoped_refptr<Extension> new_extension(
        LoadManifest("allow_silent_upgrade",
                     std::string(kTests[i].base_name) + "_new.json"));

    EXPECT_TRUE(new_extension.get()) << kTests[i].base_name << "_new.json";
    if (!new_extension.get())
      continue;

    scoped_refptr<const PermissionSet> old_p(
        old_extension->permissions_data()->active_permissions());
    scoped_refptr<const PermissionSet> new_p(
        new_extension->permissions_data()->active_permissions());
    Manifest::Type extension_type = old_extension->GetType();

    bool increased = PermissionMessageProvider::Get()->IsPrivilegeIncrease(
        old_p.get(), new_p.get(), extension_type);
    EXPECT_EQ(kTests[i].expect_increase, increased) << kTests[i].base_name;
  }
}

TEST(PermissionsTest, PermissionMessages) {
  // Ensure that all permissions that needs to show install UI actually have
  // strings associated with them.
  APIPermissionSet skip;

  // These are considered "nuisance" or "trivial" permissions that don't need
  // a prompt.
  skip.insert(APIPermission::kActiveTab);
  skip.insert(APIPermission::kAlarms);
  skip.insert(APIPermission::kAlphaEnabled);
  skip.insert(APIPermission::kAlwaysOnTopWindows);
  skip.insert(APIPermission::kAppView);
  skip.insert(APIPermission::kAudio);
  skip.insert(APIPermission::kBrowsingData);
  skip.insert(APIPermission::kCastStreaming);
  skip.insert(APIPermission::kCommandsAccessibility);
  skip.insert(APIPermission::kContextMenus);
  skip.insert(APIPermission::kCopresencePrivate);
  skip.insert(APIPermission::kDiagnostics);
  skip.insert(APIPermission::kDns);
  skip.insert(APIPermission::kDownloadsShelf);
  skip.insert(APIPermission::kEmbeddedExtensionOptions);
  skip.insert(APIPermission::kFontSettings);
  skip.insert(APIPermission::kFullscreen);
  skip.insert(APIPermission::kGcm);
  skip.insert(APIPermission::kIdle);
  skip.insert(APIPermission::kIdltest);
  skip.insert(APIPermission::kLogPrivate);
  skip.insert(APIPermission::kNotifications);
  skip.insert(APIPermission::kNotificationProvider);
  skip.insert(APIPermission::kOverrideEscFullscreen);
  skip.insert(APIPermission::kPointerLock);
  skip.insert(APIPermission::kPower);
  skip.insert(APIPermission::kPushMessaging);
  skip.insert(APIPermission::kSessions);
  skip.insert(APIPermission::kStorage);
  skip.insert(APIPermission::kSystemCpu);
  skip.insert(APIPermission::kSystemDisplay);
  skip.insert(APIPermission::kSystemMemory);
  skip.insert(APIPermission::kSystemNetwork);
  skip.insert(APIPermission::kSystemStorage);
  skip.insert(APIPermission::kTts);
  skip.insert(APIPermission::kUnlimitedStorage);
  skip.insert(APIPermission::kWebcamPrivate);
  skip.insert(APIPermission::kWebView);
  skip.insert(APIPermission::kWindowShape);

  // These permissions are restricted to extensions force-installed by policy
  // and don't require a prompt, i.e. they're restricted to location 'policy'.
  skip.insert(APIPermission::kEnterprisePlatformKeys);

  // TODO(erikkay) add a string for this permission.
  skip.insert(APIPermission::kBackground);

  skip.insert(APIPermission::kClipboardWrite);

  // The cookie permission does nothing unless you have associated host
  // permissions.
  skip.insert(APIPermission::kCookie);

  // These are warned as part of host permission checks.
  skip.insert(APIPermission::kDeclarativeContent);
  skip.insert(APIPermission::kPageCapture);
  skip.insert(APIPermission::kProxy);
  skip.insert(APIPermission::kTabCapture);
  skip.insert(APIPermission::kWebRequest);
  skip.insert(APIPermission::kWebRequestBlocking);

  // This permission requires explicit user action (context menu handler)
  // so we won't prompt for it for now.
  skip.insert(APIPermission::kFileBrowserHandler);

  // These permissions require explicit user action (configuration dialog)
  // so we don't prompt for them at install time.
  skip.insert(APIPermission::kMediaGalleries);

  // If you've turned on the experimental command-line flag, we don't need
  // to warn you further.
  skip.insert(APIPermission::kExperimental);

  // The Identity API has its own server-driven permission prompts.
  skip.insert(APIPermission::kIdentity);

  // These are private.
  skip.insert(APIPermission::kAccessibilityPrivate);
  skip.insert(APIPermission::kAutoTestPrivate);
  skip.insert(APIPermission::kBookmarkManagerPrivate);
  skip.insert(APIPermission::kBrailleDisplayPrivate);
  skip.insert(APIPermission::kCast);
  skip.insert(APIPermission::kCastStreaming);
  skip.insert(APIPermission::kChromeosInfoPrivate);
  skip.insert(APIPermission::kCloudPrintPrivate);
  skip.insert(APIPermission::kCommandLinePrivate);
  skip.insert(APIPermission::kDeveloperPrivate);
  skip.insert(APIPermission::kDial);
  skip.insert(APIPermission::kDownloadsInternal);
  skip.insert(APIPermission::kEasyUnlockPrivate);
  skip.insert(APIPermission::kEchoPrivate);
  skip.insert(APIPermission::kEnterprisePlatformKeysPrivate);
  skip.insert(APIPermission::kFeedbackPrivate);
  skip.insert(APIPermission::kFileBrowserHandlerInternal);
  skip.insert(APIPermission::kFileManagerPrivate);
  skip.insert(APIPermission::kFirstRunPrivate);
  skip.insert(APIPermission::kGcdPrivate);
  skip.insert(APIPermission::kHotwordPrivate);
  skip.insert(APIPermission::kIdentityPrivate);
  skip.insert(APIPermission::kInfobars);
  skip.insert(APIPermission::kInputMethodPrivate);
  skip.insert(APIPermission::kMediaGalleriesPrivate);
  skip.insert(APIPermission::kMediaPlayerPrivate);
  skip.insert(APIPermission::kMetricsPrivate);
  skip.insert(APIPermission::kMDns);
  skip.insert(APIPermission::kPreferencesPrivate);
  skip.insert(APIPermission::kPrincipalsPrivate);
  skip.insert(APIPermission::kImageWriterPrivate);
  skip.insert(APIPermission::kReadingListPrivate);
  skip.insert(APIPermission::kRtcPrivate);
  skip.insert(APIPermission::kStreamsPrivate);
  skip.insert(APIPermission::kSyncedNotificationsPrivate);
  skip.insert(APIPermission::kSystemPrivate);
  skip.insert(APIPermission::kTabCaptureForTab);
  skip.insert(APIPermission::kTerminalPrivate);
  skip.insert(APIPermission::kVirtualKeyboardPrivate);
  skip.insert(APIPermission::kWallpaperPrivate);
  skip.insert(APIPermission::kWebrtcAudioPrivate);
  skip.insert(APIPermission::kWebrtcLoggingPrivate);
  skip.insert(APIPermission::kWebstorePrivate);

  // Warned as part of host permissions.
  skip.insert(APIPermission::kDevtools);

  // Platform apps.
  skip.insert(APIPermission::kBrowser);
  skip.insert(APIPermission::kFileSystem);
  skip.insert(APIPermission::kFileSystemProvider);
  skip.insert(APIPermission::kFileSystemRetainEntries);
  skip.insert(APIPermission::kFileSystemWrite);
  skip.insert(APIPermission::kSocket);
  skip.insert(APIPermission::kUsbDevice);

  // We already have a generic message for declaring externally_connectable.
  skip.insert(APIPermission::kExternallyConnectableAllUrls);

  PermissionsInfo* info = PermissionsInfo::GetInstance();
  APIPermissionSet permissions = info->GetAll();
  for (APIPermissionSet::const_iterator i = permissions.begin();
       i != permissions.end(); ++i) {
    const APIPermissionInfo* permission_info = i->info();
    EXPECT_TRUE(permission_info != NULL);

    if (skip.count(i->id())) {
      EXPECT_EQ(PermissionMessage::kNone, permission_info->message_id())
          << "unexpected message_id for " << permission_info->name();
    } else {
      EXPECT_NE(PermissionMessage::kNone, permission_info->message_id())
          << "missing message_id for " << permission_info->name();
    }
  }
}

TEST(PermissionsTest, FileSystemPermissionMessages) {
  APIPermissionSet api_permissions;
  api_permissions.insert(APIPermission::kFileSystemWrite);
  api_permissions.insert(APIPermission::kFileSystemDirectory);
  scoped_refptr<PermissionSet> permissions(
      new PermissionSet(api_permissions, ManifestPermissionSet(),
                        URLPatternSet(), URLPatternSet()));
  PermissionMessages messages =
      PermissionMessageProvider::Get()->GetPermissionMessages(
          permissions.get(), Manifest::TYPE_PLATFORM_APP);
  ASSERT_EQ(1u, messages.size());
  std::sort(messages.begin(), messages.end());
  std::set<PermissionMessage::ID> ids;
  for (PermissionMessages::const_iterator it = messages.begin();
       it != messages.end(); ++it) {
    ids.insert(it->id());
  }
  EXPECT_TRUE(ContainsKey(ids, PermissionMessage::kFileSystemDirectory));
}

// The file system permissions have a special-case hack to show a warning for
// write and directory at the same time.
// TODO(sammc): Remove this. See http://crbug.com/284849.
TEST(PermissionsTest, FileSystemImplicitPermissions) {
  APIPermissionSet apis;
  apis.insert(APIPermission::kFileSystemWrite);
  apis.AddImpliedPermissions();

  EXPECT_EQ(apis.find(APIPermission::kFileSystemWrite)->id(),
            APIPermission::kFileSystemWrite);
  EXPECT_EQ(apis.size(), 1u);

  apis.erase(APIPermission::kFileSystemWrite);
  apis.insert(APIPermission::kFileSystemDirectory);
  apis.AddImpliedPermissions();

  EXPECT_EQ(apis.find(APIPermission::kFileSystemDirectory)->id(),
            APIPermission::kFileSystemDirectory);
  EXPECT_EQ(apis.size(), 1u);

  apis.insert(APIPermission::kFileSystemWrite);
  apis.AddImpliedPermissions();

  EXPECT_EQ(apis.find(APIPermission::kFileSystemWrite)->id(),
            APIPermission::kFileSystemWrite);
  EXPECT_EQ(apis.find(APIPermission::kFileSystemDirectory)->id(),
            APIPermission::kFileSystemDirectory);
  EXPECT_EQ(apis.find(APIPermission::kFileSystemWriteDirectory)->id(),
            APIPermission::kFileSystemWriteDirectory);
  EXPECT_EQ(apis.size(), 3u);
}

TEST(PermissionsTest, HiddenFileSystemPermissionMessages) {
  APIPermissionSet api_permissions;
  api_permissions.insert(APIPermission::kFileSystemWrite);
  api_permissions.insert(APIPermission::kFileSystemDirectory);
  api_permissions.insert(APIPermission::kFileSystemWriteDirectory);
  scoped_refptr<PermissionSet> permissions(
      new PermissionSet(api_permissions, ManifestPermissionSet(),
                        URLPatternSet(), URLPatternSet()));
  PermissionMessages messages =
      PermissionMessageProvider::Get()->GetPermissionMessages(
          permissions.get(), Manifest::TYPE_PLATFORM_APP);
  ASSERT_EQ(1u, messages.size());
  EXPECT_EQ(PermissionMessage::kFileSystemWriteDirectory, messages[0].id());
}

TEST(PermissionsTest, SuppressedPermissionMessages) {
  {
    // Tabs warning suppresses favicon warning.
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kTab);
    URLPatternSet hosts;
    hosts.AddPattern(URLPattern(URLPattern::SCHEME_CHROMEUI,
                                "chrome://favicon/"));
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions, ManifestPermissionSet(),
                          hosts, URLPatternSet()));
    PermissionMessages messages =
        PermissionMessageProvider::Get()->GetPermissionMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(PermissionMessage::kTabs, messages[0].id());
  }
  {
    // History warning suppresses favicon warning.
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kHistory);
    URLPatternSet hosts;
    hosts.AddPattern(URLPattern(URLPattern::SCHEME_CHROMEUI,
                                "chrome://favicon/"));
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions, ManifestPermissionSet(),
                          hosts, URLPatternSet()));
    PermissionMessages messages =
        PermissionMessageProvider::Get()->GetPermissionMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(PermissionMessage::kBrowsingHistory, messages[0].id());
  }
  {
    // All sites warning suppresses tabs warning.
    APIPermissionSet api_permissions;
    URLPatternSet hosts;
    hosts.AddPattern(URLPattern(URLPattern::SCHEME_CHROMEUI, "*://*/*"));
    api_permissions.insert(APIPermission::kTab);
    scoped_refptr<PermissionSet> permissions(new PermissionSet(
        api_permissions, ManifestPermissionSet(), hosts, URLPatternSet()));
    PermissionMessages messages =
        PermissionMessageProvider::Get()->GetPermissionMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(PermissionMessage::kHostsAll, messages[0].id());
  }
  {
    // All sites warning suppresses topSites warning.
    APIPermissionSet api_permissions;
    URLPatternSet hosts;
    hosts.AddPattern(URLPattern(URLPattern::SCHEME_CHROMEUI, "*://*/*"));
    api_permissions.insert(APIPermission::kTopSites);
    scoped_refptr<PermissionSet> permissions(new PermissionSet(
        api_permissions, ManifestPermissionSet(), hosts, URLPatternSet()));
    PermissionMessages messages =
        PermissionMessageProvider::Get()->GetPermissionMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(PermissionMessage::kHostsAll, messages[0].id());
  }
  {
    // All sites warning suppresses declarativeWebRequest warning.
    APIPermissionSet api_permissions;
    URLPatternSet hosts;
    hosts.AddPattern(URLPattern(URLPattern::SCHEME_CHROMEUI, "*://*/*"));
    api_permissions.insert(APIPermission::kDeclarativeWebRequest);
    scoped_refptr<PermissionSet> permissions(new PermissionSet(
        api_permissions, ManifestPermissionSet(), hosts, URLPatternSet()));
    PermissionMessages messages =
        PermissionMessageProvider::Get()->GetPermissionMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(PermissionMessage::kHostsAll, messages[0].id());
  }
  {
    // BrowsingHistory warning suppresses all history read/write warnings.
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kHistory);
    api_permissions.insert(APIPermission::kTab);
    api_permissions.insert(APIPermission::kTopSites);
    api_permissions.insert(APIPermission::kProcesses);
    api_permissions.insert(APIPermission::kWebNavigation);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions, ManifestPermissionSet(),
                          URLPatternSet(), URLPatternSet()));
    PermissionMessages messages =
        PermissionMessageProvider::Get()->GetPermissionMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(PermissionMessage::kBrowsingHistory, messages[0].id());
  }
  {
    // Tabs warning suppresses all read-only history warnings.
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kTab);
    api_permissions.insert(APIPermission::kTopSites);
    api_permissions.insert(APIPermission::kProcesses);
    api_permissions.insert(APIPermission::kWebNavigation);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions, ManifestPermissionSet(),
                          URLPatternSet(), URLPatternSet()));
    PermissionMessages messages =
        PermissionMessageProvider::Get()->GetPermissionMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(PermissionMessage::kTabs, messages[0].id());
  }
}

TEST(PermissionsTest, AccessToDevicesMessages) {
  {
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kUsb);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions,
                          ManifestPermissionSet(),
                          URLPatternSet(),
                          URLPatternSet()));
    std::vector<base::string16> messages =
        PermissionMessageProvider::Get()->GetWarningMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_USB),
              messages[0]);
  }
  {
    // Testing that multiple permissions will show the one message.
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kUsb);
    api_permissions.insert(APIPermission::kUsb);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions,
                          ManifestPermissionSet(),
                          URLPatternSet(),
                          URLPatternSet()));
    std::vector<base::string16> messages =
        PermissionMessageProvider::Get()->GetWarningMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_USB),
              messages[0]);
  }
  {
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kSerial);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions,
                          ManifestPermissionSet(),
                          URLPatternSet(),
                          URLPatternSet()));
    std::vector<base::string16> messages =
        PermissionMessageProvider::Get()->GetWarningMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_SERIAL),
              messages[0]);
  }
  {
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kUsb);
    api_permissions.insert(APIPermission::kSerial);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions,
                          ManifestPermissionSet(),
                          URLPatternSet(),
                          URLPatternSet()));
    std::vector<base::string16> messages =
        PermissionMessageProvider::Get()->GetWarningMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(
        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_USB_SERIAL),
        messages[0]);
  }
  {
    // Testing that the same permission(s) will show one message.
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kUsb);
    api_permissions.insert(APIPermission::kSerial);
    api_permissions.insert(APIPermission::kUsb);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions,
                          ManifestPermissionSet(),
                          URLPatternSet(),
                          URLPatternSet()));
    std::vector<base::string16> messages =
        PermissionMessageProvider::Get()->GetWarningMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(
        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_USB_SERIAL),
        messages[0]);
  }
  {
    scoped_refptr<Extension> extension =
        LoadManifest("permissions", "access_to_devices_bluetooth.json");
    const PermissionMessageProvider* provider =
        PermissionMessageProvider::Get();
    PermissionSet* set = const_cast<PermissionSet*>(
        extension->permissions_data()->active_permissions().get());
    std::vector<base::string16> warnings =
        provider->GetWarningMessages(set, extension->GetType());
    EXPECT_EQ(1u, warnings.size());
    EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_BLUETOOTH),
              warnings[0]);

    // Test Bluetooth and Serial
    set->apis_.insert(APIPermission::kSerial);
    warnings = provider->GetWarningMessages(set, extension->GetType());
    EXPECT_EQ(1u, warnings.size());
    EXPECT_EQ(l10n_util::GetStringUTF16(
                  IDS_EXTENSION_PROMPT_WARNING_BLUETOOTH_SERIAL),
              warnings[0]);
    set->apis_.erase(APIPermission::kSerial);

    // Test USB and Bluetooth
    set->apis_.insert(APIPermission::kUsb);
    warnings = provider->GetWarningMessages(set, extension->GetType());
    EXPECT_EQ(1u, warnings.size());
    EXPECT_EQ(
        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_USB_BLUETOOTH),
        warnings[0]);

    // Test USB, Bluetooth and Serial
    set->apis_.insert(APIPermission::kSerial);
    warnings = provider->GetWarningMessages(set, extension->GetType());
    EXPECT_EQ(1u, warnings.size());
    EXPECT_EQ(
        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_DEVICES),
        warnings[0]);
  }
}

TEST(PermissionsTest, MergedFileSystemPermissionComparison) {
  APIPermissionSet write_api_permissions;
  write_api_permissions.insert(APIPermission::kFileSystemWrite);
  scoped_refptr<PermissionSet> write_permissions(
      new PermissionSet(write_api_permissions, ManifestPermissionSet(),
                        URLPatternSet(), URLPatternSet()));

  APIPermissionSet directory_api_permissions;
  directory_api_permissions.insert(APIPermission::kFileSystemDirectory);
  scoped_refptr<PermissionSet> directory_permissions(
      new PermissionSet(directory_api_permissions, ManifestPermissionSet(),
                        URLPatternSet(), URLPatternSet()));

  APIPermissionSet write_directory_api_permissions;
  write_directory_api_permissions.insert(
      APIPermission::kFileSystemWriteDirectory);
  scoped_refptr<PermissionSet> write_directory_permissions(
      new PermissionSet(write_directory_api_permissions,
                        ManifestPermissionSet(),
                        URLPatternSet(),
                        URLPatternSet()));

  const PermissionMessageProvider* provider = PermissionMessageProvider::Get();
  EXPECT_FALSE(provider->IsPrivilegeIncrease(write_directory_permissions.get(),
                                             write_permissions.get(),
                                             Manifest::TYPE_PLATFORM_APP));
  EXPECT_FALSE(provider->IsPrivilegeIncrease(write_directory_permissions.get(),
                                             directory_permissions.get(),
                                             Manifest::TYPE_PLATFORM_APP));
  EXPECT_TRUE(provider->IsPrivilegeIncrease(write_permissions.get(),
                                            directory_permissions.get(),
                                            Manifest::TYPE_PLATFORM_APP));
  EXPECT_TRUE(provider->IsPrivilegeIncrease(write_permissions.get(),
                                            write_directory_permissions.get(),
                                            Manifest::TYPE_PLATFORM_APP));
  EXPECT_FALSE(provider->IsPrivilegeIncrease(directory_permissions.get(),
                                             write_permissions.get(),
                                             Manifest::TYPE_PLATFORM_APP));
  EXPECT_TRUE(provider->IsPrivilegeIncrease(directory_permissions.get(),
                                            write_directory_permissions.get(),
                                            Manifest::TYPE_PLATFORM_APP));
}

TEST(PermissionsTest, GetWarningMessages_ManyHosts) {
  scoped_refptr<Extension> extension;

  extension = LoadManifest("permissions", "many-hosts.json");
  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();
  ASSERT_EQ(1u, warnings.size());
  EXPECT_EQ(
      "Read and change your data on encrypted.google.com and "
      "www.google.com",
      base::UTF16ToUTF8(warnings[0]));
}

TEST(PermissionsTest, GetWarningMessages_Plugins) {
  scoped_refptr<Extension> extension;
  scoped_refptr<PermissionSet> permissions;

  extension = LoadManifest("permissions", "plugins.json");
  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();
// We don't parse the plugins key on Chrome OS, so it should not ask for any
  // permissions.
#if defined(OS_CHROMEOS)
  ASSERT_EQ(0u, warnings.size());
#else
  ASSERT_EQ(1u, warnings.size());
  EXPECT_EQ(
      "Read and change all your data on your computer and the websites "
      "you visit",
      base::UTF16ToUTF8(warnings[0]));
#endif
}

TEST(PermissionsTest, GetWarningMessages_AudioVideo) {
  // Both audio and video present.
  scoped_refptr<Extension> extension =
      LoadManifest("permissions", "audio-video.json");
  const PermissionMessageProvider* provider = PermissionMessageProvider::Get();
  PermissionSet* set = const_cast<PermissionSet*>(
      extension->permissions_data()->active_permissions().get());
  std::vector<base::string16> warnings =
      provider->GetWarningMessages(set, extension->GetType());
  EXPECT_FALSE(Contains(warnings, "Use your microphone"));
  EXPECT_FALSE(Contains(warnings, "Use your camera"));
  EXPECT_TRUE(Contains(warnings, "Use your microphone and camera"));
  size_t combined_index = IndexOf(warnings, "Use your microphone and camera");
  size_t combined_size = warnings.size();

  // Just audio present.
  set->apis_.erase(APIPermission::kVideoCapture);
  warnings = provider->GetWarningMessages(set, extension->GetType());
  EXPECT_EQ(combined_size, warnings.size());
  EXPECT_EQ(combined_index, IndexOf(warnings, "Use your microphone"));
  EXPECT_FALSE(Contains(warnings, "Use your camera"));
  EXPECT_FALSE(Contains(warnings, "Use your microphone and camera"));

  // Just video present.
  set->apis_.erase(APIPermission::kAudioCapture);
  set->apis_.insert(APIPermission::kVideoCapture);
  warnings = provider->GetWarningMessages(set, extension->GetType());
  EXPECT_EQ(combined_size, warnings.size());
  EXPECT_FALSE(Contains(warnings, "Use your microphone"));
  EXPECT_FALSE(Contains(warnings, "Use your microphone and camera"));
  EXPECT_TRUE(Contains(warnings, "Use your camera"));
}

TEST(PermissionsTest, GetWarningMessages_CombinedSessions) {
  {
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kTab);
    api_permissions.insert(APIPermission::kTopSites);
    api_permissions.insert(APIPermission::kProcesses);
    api_permissions.insert(APIPermission::kWebNavigation);
    api_permissions.insert(APIPermission::kSessions);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions, ManifestPermissionSet(),
                          URLPatternSet(), URLPatternSet()));
    std::vector<base::string16> messages =
        PermissionMessageProvider::Get()->GetWarningMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(l10n_util::GetStringUTF16(
                  IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ_AND_SESSIONS),
              messages[0]);
  }
  {
    APIPermissionSet api_permissions;
    api_permissions.insert(APIPermission::kHistory);
    api_permissions.insert(APIPermission::kTab);
    api_permissions.insert(APIPermission::kTopSites);
    api_permissions.insert(APIPermission::kProcesses);
    api_permissions.insert(APIPermission::kWebNavigation);
    api_permissions.insert(APIPermission::kSessions);
    scoped_refptr<PermissionSet> permissions(
        new PermissionSet(api_permissions, ManifestPermissionSet(),
                          URLPatternSet(), URLPatternSet()));
    std::vector<base::string16> messages =
        PermissionMessageProvider::Get()->GetWarningMessages(
            permissions.get(), Manifest::TYPE_EXTENSION);
    EXPECT_EQ(1u, messages.size());
    EXPECT_EQ(l10n_util::GetStringUTF16(
                  IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE_AND_SESSIONS),
              messages[0]);
  }
}

TEST(PermissionsTest, GetWarningMessages_DeclarativeWebRequest) {
  // Test that if the declarativeWebRequest permission is present
  // in combination with all hosts permission, then only the warning
  // for host permissions is shown, because that covers the use of
  // declarativeWebRequest.

  // Until Declarative Web Request is in stable, let's make sure it is enabled
  // on the current channel.
  ScopedCurrentChannel sc(chrome::VersionInfo::CHANNEL_CANARY);

  // First verify that declarativeWebRequest produces a message when host
  // permissions do not cover all hosts.
  scoped_refptr<Extension> extension =
      LoadManifest("permissions", "web_request_not_all_host_permissions.json");
  const PermissionMessageProvider* provider = PermissionMessageProvider::Get();
  const PermissionSet* set =
      extension->permissions_data()->active_permissions().get();
  std::vector<base::string16> warnings =
      provider->GetWarningMessages(set, extension->GetType());
  EXPECT_TRUE(Contains(warnings, "Block parts of web pages"));
  EXPECT_FALSE(Contains(
      warnings, "Read and change all your data on the websites you visit"));

  // Now verify that declarativeWebRequest does not produce a message when host
  // permissions do cover all hosts.
  extension =
      LoadManifest("permissions", "web_request_all_host_permissions.json");
  set = extension->permissions_data()->active_permissions().get();
  warnings = provider->GetWarningMessages(set, extension->GetType());
  EXPECT_FALSE(Contains(warnings, "Block parts of web pages"));
  EXPECT_TRUE(Contains(
      warnings, "Read and change all your data on the websites you visit"));
}

TEST(PermissionsTest, GetWarningMessages_Serial) {
  scoped_refptr<Extension> extension =
      LoadManifest("permissions", "serial.json");

  EXPECT_TRUE(extension->is_platform_app());
  EXPECT_TRUE(
      extension->permissions_data()->HasAPIPermission(APIPermission::kSerial));
  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();
  EXPECT_TRUE(Contains(warnings, "Access your serial devices"));
  ASSERT_EQ(1u, warnings.size());
}

TEST(PermissionsTest, GetWarningMessages_Socket_AnyHost) {
  ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_DEV);

  scoped_refptr<Extension> extension =
      LoadManifest("permissions", "socket_any_host.json");
  EXPECT_TRUE(extension->is_platform_app());
  EXPECT_TRUE(
      extension->permissions_data()->HasAPIPermission(APIPermission::kSocket));
  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();
  EXPECT_EQ(1u, warnings.size());
  EXPECT_TRUE(Contains(warnings, "Exchange data with any computer "
                                 "on the local network or internet"));
}

TEST(PermissionsTest, GetWarningMessages_Socket_OneDomainTwoHostnames) {
  ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_DEV);

  scoped_refptr<Extension> extension =
      LoadManifest("permissions", "socket_one_domain_two_hostnames.json");
  EXPECT_TRUE(extension->is_platform_app());
  EXPECT_TRUE(
      extension->permissions_data()->HasAPIPermission(APIPermission::kSocket));
  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();

  // Verify the warnings, including support for unicode characters, the fact
  // that domain host warnings come before specific host warnings, and the fact
  // that domains and hostnames are in alphabetical order regardless of the
  // order in the manifest file.
  EXPECT_EQ(2u, warnings.size());
  if (warnings.size() > 0)
    EXPECT_EQ(warnings[0],
              base::UTF8ToUTF16("Exchange data with any computer in the domain "
                          "example.org"));
  if (warnings.size() > 1)
    EXPECT_EQ(warnings[1],
              base::UTF8ToUTF16("Exchange data with the computers named: "
                          "b\xC3\xA5r.example.com foo.example.com"));
                          // "\xC3\xA5" = UTF-8 for lowercase A with ring above
}

TEST(PermissionsTest, GetWarningMessages_Socket_TwoDomainsOneHostname) {
  ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_DEV);

  scoped_refptr<Extension> extension =
      LoadManifest("permissions", "socket_two_domains_one_hostname.json");
  EXPECT_TRUE(extension->is_platform_app());
  EXPECT_TRUE(
      extension->permissions_data()->HasAPIPermission(APIPermission::kSocket));
  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();

  // Verify the warnings, including the fact that domain host warnings come
  // before specific host warnings and the fact that domains and hostnames are
  // in alphabetical order regardless of the order in the manifest file.
  EXPECT_EQ(2u, warnings.size());
  if (warnings.size() > 0)
    EXPECT_EQ(warnings[0],
              base::UTF8ToUTF16("Exchange data with any computer in the "
                                "domains: example.com foo.example.org"));
  if (warnings.size() > 1)
    EXPECT_EQ(warnings[1],
              base::UTF8ToUTF16("Exchange data with the computer named "
                                "bar.example.org"));
}

TEST(PermissionsTest, GetWarningMessages_PlatformApppHosts) {
  scoped_refptr<Extension> extension;

  extension = LoadManifest("permissions", "platform_app_hosts.json");
  EXPECT_TRUE(extension->is_platform_app());
  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();
  ASSERT_EQ(0u, warnings.size());

  extension = LoadManifest("permissions", "platform_app_all_urls.json");
  EXPECT_TRUE(extension->is_platform_app());
  warnings = extension->permissions_data()->GetPermissionMessageStrings();
  ASSERT_EQ(0u, warnings.size());
}

bool ShowsAllHostsWarning(const std::string& pattern) {
  scoped_refptr<Extension> extension =
      ExtensionBuilder()
          .SetManifest(DictionaryBuilder()
                           .Set("name", "TLDWildCardTest")
                           .Set("version", "0.1.0")
                           .Set("permissions", ListBuilder().Append(pattern))
                           .Build())
          .Build();

  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();

  if (warnings.empty())
    return false;

  if (warnings[0] !=
      l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)) {
    return false;
  }

  return true;
}

TEST(PermissionsTest, GetWarningMessages_TLDWildcardTreatedAsAllHosts) {
  EXPECT_TRUE(ShowsAllHostsWarning("http://*.com/*"));    // most popular.
  EXPECT_TRUE(ShowsAllHostsWarning("http://*.org/*"));    // sanity check.
  EXPECT_TRUE(ShowsAllHostsWarning("http://*.co.uk/*"));  // eTLD.
  EXPECT_TRUE(ShowsAllHostsWarning("http://*.de/*"));  // foreign country tld.

  // We should still show the normal permissions (i.e., "Can access your data on
  // *.rdcronin.com") for things that are not TLDs.
  EXPECT_FALSE(ShowsAllHostsWarning("http://*.rdcronin.com/*"));

  // Pseudo-TLDs, like appspot.com, should not show all hosts.
  EXPECT_FALSE(ShowsAllHostsWarning("http://*.appspot.com/*"));

  // Non-TLDs should be likewise exempt.
  EXPECT_FALSE(ShowsAllHostsWarning("http://*.notarealtld/*"));

  // Our internal checks use "foo", so let's make sure we're not messing
  // something up with it.
  EXPECT_FALSE(ShowsAllHostsWarning("http://*.foo.com"));
  EXPECT_FALSE(ShowsAllHostsWarning("http://foo.com"));
  // This will fail if foo becomes a recognized TLD. Which could be soon.
  // Update as needed.
  EXPECT_FALSE(ShowsAllHostsWarning("http://*.foo"));
}

TEST(PermissionsTest, GetDistinctHosts) {
  URLPatternSet explicit_hosts;
  std::set<std::string> expected;
  expected.insert("www.foo.com");
  expected.insert("www.bar.com");
  expected.insert("www.baz.com");

  {
    SCOPED_TRACE("no dupes");

    // Simple list with no dupes.
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  explicit_hosts, true, true));
  }

  {
    SCOPED_TRACE("two dupes");

    // Add some dupes.
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  explicit_hosts, true, true));
  }

  {
    SCOPED_TRACE("schemes differ");

    // Add a pattern that differs only by scheme. This should be filtered out.
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path"));
    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  explicit_hosts, true, true));
  }

  {
    SCOPED_TRACE("paths differ");

    // Add some dupes by path.
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath"));
    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  explicit_hosts, true, true));
  }

  {
    SCOPED_TRACE("subdomains differ");

    // We don't do anything special for subdomains.
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://monkey.www.bar.com/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://bar.com/path"));

    expected.insert("monkey.www.bar.com");
    expected.insert("bar.com");

    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  explicit_hosts, true, true));
  }

  {
    SCOPED_TRACE("RCDs differ");

    // Now test for RCD uniquing.
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.de/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca.us/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com.my/path"));

    // This is an unknown RCD, which shouldn't be uniqued out.
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));
    // But it should only occur once.
    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));

    expected.insert("www.foo.xyzzy");

    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  explicit_hosts, true, true));
  }

  {
    SCOPED_TRACE("wildcards");

    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*"));

    expected.insert("*.google.com");

    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  explicit_hosts, true, true));
  }

  {
    SCOPED_TRACE("scriptable hosts");

    APIPermissionSet empty_perms;
    explicit_hosts.ClearPatterns();
    URLPatternSet scriptable_hosts;
    expected.clear();

    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*"));
    scriptable_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_HTTP, "http://*.example.com/*"));

    expected.insert("*.google.com");
    expected.insert("*.example.com");

    scoped_refptr<PermissionSet> perm_set(new PermissionSet(
        empty_perms, ManifestPermissionSet(),
        explicit_hosts, scriptable_hosts));
    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  perm_set->effective_hosts(), true, true));
  }

  {
    // We don't display warnings for file URLs because they are off by default.
    SCOPED_TRACE("file urls");

    explicit_hosts.ClearPatterns();
    expected.clear();

    explicit_hosts.AddPattern(
        URLPattern(URLPattern::SCHEME_FILE, "file:///*"));

    EXPECT_EQ(expected,
              permission_message_util::GetDistinctHosts(
                  explicit_hosts, true, true));
  }
}

TEST(PermissionsTest, GetDistinctHosts_ComIsBestRcd) {
  URLPatternSet explicit_hosts;
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));

  std::set<std::string> expected;
  expected.insert("www.foo.com");
  EXPECT_EQ(expected,
            permission_message_util::GetDistinctHosts(
                explicit_hosts, true, true));
}

TEST(PermissionsTest, GetDistinctHosts_NetIs2ndBestRcd) {
  URLPatternSet explicit_hosts;
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
  // No http://www.foo.com/path

  std::set<std::string> expected;
  expected.insert("www.foo.net");
  EXPECT_EQ(expected,
            permission_message_util::GetDistinctHosts(
                explicit_hosts, true, true));
}

TEST(PermissionsTest, GetDistinctHosts_OrgIs3rdBestRcd) {
  URLPatternSet explicit_hosts;
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
  // No http://www.foo.net/path
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
  // No http://www.foo.com/path

  std::set<std::string> expected;
  expected.insert("www.foo.org");
  EXPECT_EQ(expected,
            permission_message_util::GetDistinctHosts(
                explicit_hosts, true, true));
}

TEST(PermissionsTest, GetDistinctHosts_FirstInListIs4thBestRcd) {
  URLPatternSet explicit_hosts;
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
  // No http://www.foo.org/path
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
  // No http://www.foo.net/path
  explicit_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
  // No http://www.foo.com/path

  std::set<std::string> expected;
  expected.insert("www.foo.ca");
  EXPECT_EQ(expected,
            permission_message_util::GetDistinctHosts(
                explicit_hosts, true, true));
}

TEST(PermissionsTest, IsHostPrivilegeIncrease) {
  Manifest::Type type = Manifest::TYPE_EXTENSION;
  const PermissionMessageProvider* provider = PermissionMessageProvider::Get();
  ManifestPermissionSet empty_manifest_permissions;
  URLPatternSet elist1;
  URLPatternSet elist2;
  URLPatternSet slist1;
  URLPatternSet slist2;
  scoped_refptr<PermissionSet> set1;
  scoped_refptr<PermissionSet> set2;
  APIPermissionSet empty_perms;
  elist1.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
  elist1.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));

  // Test that the host order does not matter.
  elist2.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
  elist2.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));

  set1 = new PermissionSet(empty_perms, empty_manifest_permissions,
                           elist1, slist1);
  set2 = new PermissionSet(empty_perms, empty_manifest_permissions,
                           elist2, slist2);

  EXPECT_FALSE(provider->IsPrivilegeIncrease(set1.get(), set2.get(), type));
  EXPECT_FALSE(provider->IsPrivilegeIncrease(set2.get(), set1.get(), type));

  // Test that paths are ignored.
  elist2.ClearPatterns();
  elist2.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*"));
  set2 = new PermissionSet(empty_perms, empty_manifest_permissions,
                           elist2, slist2);
  EXPECT_FALSE(provider->IsPrivilegeIncrease(set1.get(), set2.get(), type));
  EXPECT_FALSE(provider->IsPrivilegeIncrease(set2.get(), set1.get(), type));

  // Test that RCDs are ignored.
  elist2.ClearPatterns();
  elist2.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*"));
  set2 = new PermissionSet(empty_perms, empty_manifest_permissions,
                           elist2, slist2);
  EXPECT_FALSE(provider->IsPrivilegeIncrease(set1.get(), set2.get(), type));
  EXPECT_FALSE(provider->IsPrivilegeIncrease(set2.get(), set1.get(), type));

  // Test that subdomain wildcards are handled properly.
  elist2.ClearPatterns();
  elist2.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*"));
  set2 = new PermissionSet(empty_perms, empty_manifest_permissions,
                           elist2, slist2);
  EXPECT_TRUE(provider->IsPrivilegeIncrease(set1.get(), set2.get(), type));
  // TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337
  // EXPECT_FALSE(provider->IsPrivilegeIncrease(set2, set1, type));

  // Test that different domains count as different hosts.
  elist2.ClearPatterns();
  elist2.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
  elist2.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path"));
  set2 = new PermissionSet(empty_perms, empty_manifest_permissions,
                           elist2, slist2);
  EXPECT_TRUE(provider->IsPrivilegeIncrease(set1.get(), set2.get(), type));
  EXPECT_FALSE(provider->IsPrivilegeIncrease(set2.get(), set1.get(), type));

  // Test that different subdomains count as different hosts.
  elist2.ClearPatterns();
  elist2.AddPattern(
      URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*"));
  set2 = new PermissionSet(empty_perms, empty_manifest_permissions,
                           elist2, slist2);
  EXPECT_TRUE(provider->IsPrivilegeIncrease(set1.get(), set2.get(), type));
  EXPECT_TRUE(provider->IsPrivilegeIncrease(set2.get(), set1.get(), type));

  // Test that platform apps do not have host permissions increases.
  type = Manifest::TYPE_PLATFORM_APP;
  EXPECT_FALSE(provider->IsPrivilegeIncrease(set1.get(), set2.get(), type));
  EXPECT_FALSE(provider->IsPrivilegeIncrease(set2.get(), set1.get(), type));
}

TEST(PermissionsTest, GetAPIsAsStrings) {
  APIPermissionSet apis;
  URLPatternSet empty_set;

  apis.insert(APIPermission::kProxy);
  apis.insert(APIPermission::kBackground);
  apis.insert(APIPermission::kNotifications);
  apis.insert(APIPermission::kTab);

  scoped_refptr<PermissionSet> perm_set = new PermissionSet(
      apis, ManifestPermissionSet(), empty_set, empty_set);
  std::set<std::string> api_names = perm_set->GetAPIsAsStrings();

  // The result is correct if it has the same number of elements
  // and we can convert it back to the id set.
  EXPECT_EQ(4u, api_names.size());
  EXPECT_EQ(apis,
            PermissionsInfo::GetInstance()->GetAllByName(api_names));
}

TEST(PermissionsTest, IsEmpty) {
  APIPermissionSet empty_apis;
  URLPatternSet empty_extent;

  scoped_refptr<PermissionSet> empty = new PermissionSet();
  EXPECT_TRUE(empty->IsEmpty());
  scoped_refptr<PermissionSet> perm_set;

  perm_set = new PermissionSet(empty_apis, ManifestPermissionSet(),
                               empty_extent, empty_extent);
  EXPECT_TRUE(perm_set->IsEmpty());

  APIPermissionSet non_empty_apis;
  non_empty_apis.insert(APIPermission::kBackground);
  perm_set = new PermissionSet(non_empty_apis, ManifestPermissionSet(),
                               empty_extent, empty_extent);
  EXPECT_FALSE(perm_set->IsEmpty());

  // Try non standard host
  URLPatternSet non_empty_extent;
  AddPattern(&non_empty_extent, "http://www.google.com/*");

  perm_set = new PermissionSet(empty_apis, ManifestPermissionSet(),
                               non_empty_extent, empty_extent);
  EXPECT_FALSE(perm_set->IsEmpty());

  perm_set = new PermissionSet(empty_apis, ManifestPermissionSet(),
                               empty_extent, non_empty_extent);
  EXPECT_FALSE(perm_set->IsEmpty());
}

TEST(PermissionsTest, ImpliedPermissions) {
  URLPatternSet empty_extent;
  APIPermissionSet apis;
  apis.insert(APIPermission::kFileBrowserHandler);
  EXPECT_EQ(1U, apis.size());

  scoped_refptr<PermissionSet> perm_set;
  perm_set = new PermissionSet(apis, ManifestPermissionSet(),
                               empty_extent, empty_extent);
  EXPECT_EQ(2U, perm_set->apis().size());
}

TEST(PermissionsTest, SyncFileSystemPermission) {
  scoped_refptr<Extension> extension = LoadManifest(
      "permissions", "sync_file_system.json");
  APIPermissionSet apis;
  apis.insert(APIPermission::kSyncFileSystem);
  EXPECT_TRUE(extension->is_platform_app());
  EXPECT_TRUE(extension->permissions_data()->HasAPIPermission(
      APIPermission::kSyncFileSystem));
  std::vector<base::string16> warnings =
      extension->permissions_data()->GetPermissionMessageStrings();
  EXPECT_TRUE(Contains(warnings, "Store data in your Google Drive account"));
  ASSERT_EQ(1u, warnings.size());
}

// Make sure that we don't crash when we're trying to show the permissions
// even though chrome://thumb (and everything that's not chrome://favicon with
// a chrome:// scheme) is not a valid permission.
// More details here: crbug/246314.
TEST(PermissionsTest, ChromeURLs) {
  URLPatternSet allowed_hosts;
  allowed_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_ALL, "http://www.google.com/"));
  allowed_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_ALL, "chrome://favicon/"));
  allowed_hosts.AddPattern(
      URLPattern(URLPattern::SCHEME_ALL, "chrome://thumb/"));
  scoped_refptr<PermissionSet> permissions(
      new PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
                        allowed_hosts, URLPatternSet()));
  PermissionMessageProvider::Get()->GetPermissionMessages(
      permissions.get(), Manifest::TYPE_EXTENSION);
}

TEST(PermissionsTest, IsPrivilegeIncrease_DeclarativeWebRequest) {
  scoped_refptr<Extension> extension(
      LoadManifest("permissions", "permissions_all_urls.json"));
  scoped_refptr<const PermissionSet> permissions(
      extension->permissions_data()->active_permissions());

  scoped_refptr<Extension> extension_dwr(
      LoadManifest("permissions", "web_request_all_host_permissions.json"));
  scoped_refptr<const PermissionSet> permissions_dwr(
      extension_dwr->permissions_data()->active_permissions());

  EXPECT_FALSE(PermissionMessageProvider::Get()->
                   IsPrivilegeIncrease(permissions.get(),
                                       permissions_dwr.get(),
                                       extension->GetType()));
}

}  // namespace extensions
