blob: 6a24dd4a3e0629db9acb0d9fc6b1257880298c73 [file] [log] [blame]
// Copyright (c) 2013 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 "chrome/browser/extensions/extension_service.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_file_value_serializer.h"
#include "base/json/json_reader.h"
#include "base/json/json_string_value_serializer.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/pattern.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "base/version.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/blacklist.h"
#include "chrome/browser/extensions/chrome_app_sorting.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/default_apps.h"
#include "chrome/browser/extensions/extension_error_ui.h"
#include "chrome/browser/extensions/extension_management_test_util.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/extension_service_test_with_install.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/external_install_error.h"
#include "chrome/browser/extensions/external_install_manager.h"
#include "chrome/browser/extensions/external_policy_loader.h"
#include "chrome/browser/extensions/external_pref_loader.h"
#include "chrome/browser/extensions/external_provider_impl.h"
#include "chrome/browser/extensions/fake_safe_browsing_database_manager.h"
#include "chrome/browser/extensions/installed_loader.h"
#include "chrome/browser/extensions/load_error_reporter.h"
#include "chrome/browser/extensions/pack_extension_job.h"
#include "chrome/browser/extensions/pending_extension_info.h"
#include "chrome/browser/extensions/pending_extension_manager.h"
#include "chrome/browser/extensions/permissions_test_util.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/test_blacklist.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/policy/profile_policy_connector_factory.h"
#include "chrome/browser/ui/global_error/global_error.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/scoped_browser_locale.h"
#include "chrome/test/base/testing_profile.h"
#include "components/crx_file/id_util.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/sync/model/string_ordinal.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/dom_storage_context.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/indexed_db_context.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_constants.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_creator.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/external_install_info.h"
#include "extensions/browser/external_provider_interface.h"
#include "extensions/browser/install_flag.h"
#include "extensions/browser/management_policy.h"
#include "extensions/browser/mock_external_provider.h"
#include "extensions/browser/pref_names.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/browser/test_management_policy.h"
#include "extensions/browser/uninstall_reason.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_l10n_util.h"
#include "extensions/common/extension_resource.h"
#include "extensions/common/file_util.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/manifest_handlers/content_scripts_handler.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/manifest_url_handlers.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/switches.h"
#include "extensions/common/url_pattern.h"
#include "extensions/common/value_builder.h"
#include "extensions/common/verifier_formats.h"
#include "extensions/test/test_extension_dir.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_options.h"
#include "net/cookies/cookie_store.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "ppapi/buildflags/buildflags.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "storage/browser/database/database_tracker.h"
#include "storage/browser/quota/quota_manager.h"
#include "storage/common/database/database_identifier.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#include "url/origin.h"
// The blacklist tests rely on the safe-browsing database.
#if defined(SAFE_BROWSING_DB_LOCAL)
#define ENABLE_BLACKLIST_TESTS
#endif
using content::BrowserContext;
using content::BrowserThread;
using content::DOMStorageContext;
using content::IndexedDBContext;
using content::PluginService;
namespace extensions {
namespace keys = manifest_keys;
namespace {
// Extension ids used during testing.
const char good0[] = "behllobkkfkfnphdnhnkndlbkcpglgmj";
const char good1[] = "hpiknbiabeeppbpihjehijgoemciehgk";
const char good2[] = "bjafgdebaacbbbecmhlhpofkepfkgcpa";
const char all_zero[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const char good2048[] = "dfhpodpjggiioolfhoimofdbfjibmedp";
const char good_crx[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
const char minimal_platform_app_crx[] = "jjeoclcdfjddkdjokiejckgcildcflpp";
const char hosted_app[] = "kbmnembihfiondgfjekmnmcbddelicoi";
const char page_action[] = "dpfmafkdlbmopmcepgpjkpldjbghdibm";
const char theme_crx[] = "idlfhncioikpdnlhnmcjogambnefbbfp";
const char theme2_crx[] = "ibcijncamhmjjdodjamgiipcgnnaeagd";
const char permissions_crx[] = "eagpmdpfmaekmmcejjbmjoecnejeiiin";
const char updates_from_webstore[] = "akjooamlhcgeopfifcmlggaebeocgokj";
const char updates_from_webstore2[] = "oolblhbomdbcpmafphaodhjfcgbihcdg";
const char updates_from_webstore3[] = "bmfoocgfinpmkmlbjhcbofejhkhlbchk";
const char permissions_blocklist[] = "noffkehfcaggllbcojjbopcmlhcnhcdn";
const char cast_stable[] = "boadgeojelhgndaghljhdicfkmllpafd";
const char cast_beta[] = "dliochdbjfkdbacpmhlcpmleaejidimm";
struct BubbleErrorsTestData {
BubbleErrorsTestData(const std::string& id,
const std::string& version,
const base::FilePath& crx_path,
size_t expected_bubble_error_count)
: id(id),
version(version),
crx_path(crx_path),
expected_bubble_error_count(expected_bubble_error_count) {}
std::string id;
std::string version;
base::FilePath crx_path;
size_t expected_bubble_error_count;
bool expect_has_shown_bubble_view;
};
static void AddPattern(URLPatternSet* extent, const std::string& pattern) {
int schemes = URLPattern::SCHEME_ALL;
extent->AddPattern(URLPattern(schemes, pattern));
}
base::FilePath GetTemporaryFile() {
base::FilePath temp_file;
CHECK(base::CreateTemporaryFile(&temp_file));
return temp_file;
}
bool WaitForCountNotificationsCallback(int *count) {
return --(*count) == 0;
}
bool HasExternalInstallErrors(ExtensionService* service) {
return !service->external_install_manager()->GetErrorsForTesting().empty();
}
bool HasExternalInstallBubble(ExtensionService* service) {
std::vector<ExternalInstallError*> errors =
service->external_install_manager()->GetErrorsForTesting();
auto found = std::find_if(
errors.begin(), errors.end(),
[](const ExternalInstallError* error) {
return error->alert_type() == ExternalInstallError::BUBBLE_ALERT;
});
return found != errors.end();
}
size_t GetExternalInstallBubbleCount(ExtensionService* service) {
size_t bubble_count = 0u;
std::vector<ExternalInstallError*> errors =
service->external_install_manager()->GetErrorsForTesting();
for (auto* error : errors)
bubble_count += error->alert_type() == ExternalInstallError::BUBBLE_ALERT;
return bubble_count;
}
scoped_refptr<const Extension> CreateExtension(const std::string& name,
const base::FilePath& path,
Manifest::Location location) {
return ExtensionBuilder(name).SetPath(path).SetLocation(location).Build();
}
std::unique_ptr<ExternalInstallInfoFile> CreateExternalExtension(
const ExtensionId& extension_id,
const std::string& version_str,
const base::FilePath& path,
Manifest::Location location,
Extension::InitFromValueFlags flags) {
return std::make_unique<ExternalInstallInfoFile>(
extension_id, base::Version(version_str), path, location, flags, false,
false);
}
// Helper function to persist the passed directories and file paths in
// |extension_dir|. Also, writes a generic manifest file.
void PersistExtensionWithPaths(
const base::FilePath& extension_dir,
const std::vector<base::FilePath>& directory_paths,
const std::vector<base::FilePath>& file_paths) {
for (const auto& directory : directory_paths)
EXPECT_TRUE(base::CreateDirectory(directory));
std::string data = "file_data";
for (const auto& file : file_paths) {
EXPECT_EQ(static_cast<int>(data.size()),
base::WriteFile(file, data.c_str(), data.size()));
}
std::unique_ptr<base::DictionaryValue> manifest =
DictionaryBuilder()
.Set(keys::kName, "Test extension")
.Set(keys::kVersion, "1.0")
.Set(keys::kManifestVersion, 2)
.Build();
// Persist manifest file.
base::FilePath manifest_path = extension_dir.Append(kManifestFilename);
JSONFileValueSerializer(manifest_path).Serialize(*manifest);
EXPECT_TRUE(base::PathExists(manifest_path));
}
} // namespace
// A simplified version of ExternalPrefLoader that loads the dictionary
// from json data specified in a string.
class ExternalTestingLoader : public ExternalLoader {
public:
ExternalTestingLoader(const std::string& json_data,
const base::FilePath& fake_base_path)
: fake_base_path_(fake_base_path) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JSONStringValueDeserializer deserializer(json_data);
base::FilePath fake_json_path = fake_base_path.AppendASCII("fake.json");
testing_prefs_ = ExternalPrefLoader::ExtractExtensionPrefs(&deserializer,
fake_json_path);
}
// ExternalLoader:
const base::FilePath GetBaseCrxFilePath() override { return fake_base_path_; }
void StartLoading() override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
LoadFinished(testing_prefs_->CreateDeepCopy());
}
private:
friend class base::RefCountedThreadSafe<ExternalLoader>;
~ExternalTestingLoader() override {}
base::FilePath fake_base_path_;
std::unique_ptr<base::DictionaryValue> testing_prefs_;
DISALLOW_COPY_AND_ASSIGN(ExternalTestingLoader);
};
class MockProviderVisitor : public ExternalProviderInterface::VisitorInterface {
public:
// The provider will return |fake_base_path| from
// GetBaseCrxFilePath(). User can test the behavior with
// and without an empty path using this parameter.
explicit MockProviderVisitor(base::FilePath fake_base_path)
: ids_found_(0),
fake_base_path_(fake_base_path),
expected_creation_flags_(Extension::NO_FLAGS) {
profile_.reset(new TestingProfile);
}
MockProviderVisitor(base::FilePath fake_base_path,
int expected_creation_flags)
: ids_found_(0),
fake_base_path_(fake_base_path),
expected_creation_flags_(expected_creation_flags) {
profile_.reset(new TestingProfile);
}
int Visit(const std::string& json_data) {
return Visit(json_data, Manifest::EXTERNAL_PREF,
Manifest::EXTERNAL_PREF_DOWNLOAD);
}
int Visit(const std::string& json_data,
Manifest::Location crx_location,
Manifest::Location download_location) {
crx_location_ = crx_location;
// Give the test json file to the provider for parsing.
provider_.reset(new ExternalProviderImpl(
this, new ExternalTestingLoader(json_data, fake_base_path_),
profile_.get(), crx_location, download_location, Extension::NO_FLAGS));
if (crx_location == Manifest::EXTERNAL_REGISTRY)
provider_->set_allow_updates(true);
// We also parse the file into a dictionary to compare what we get back
// from the provider.
prefs_ = GetDictionaryFromJSON(json_data);
// Reset our counter.
ids_found_ = 0;
// Ask the provider to look up all extensions and return them.
provider_->VisitRegisteredExtension();
return ids_found_;
}
bool OnExternalExtensionFileFound(
const ExternalInstallInfoFile& info) override {
EXPECT_EQ(expected_creation_flags_, info.creation_flags);
++ids_found_;
base::DictionaryValue* pref;
// This tests is to make sure that the provider only notifies us of the
// values we gave it. So if the id we doesn't exist in our internal
// dictionary then something is wrong.
EXPECT_TRUE(prefs_->GetDictionary(info.extension_id, &pref))
<< "Got back ID (" << info.extension_id.c_str()
<< ") we weren't expecting";
EXPECT_TRUE(info.path.IsAbsolute());
if (!fake_base_path_.empty())
EXPECT_TRUE(fake_base_path_.IsParent(info.path));
if (pref) {
EXPECT_TRUE(provider_->HasExtension(info.extension_id));
// Ask provider if the extension we got back is registered.
Manifest::Location location = Manifest::INVALID_LOCATION;
std::unique_ptr<base::Version> v1;
base::FilePath crx_path;
EXPECT_TRUE(provider_->GetExtensionDetails(info.extension_id, NULL, &v1));
EXPECT_EQ(info.version.GetString(), v1->GetString());
std::unique_ptr<base::Version> v2;
EXPECT_TRUE(
provider_->GetExtensionDetails(info.extension_id, &location, &v2));
EXPECT_EQ(info.version.GetString(), v1->GetString());
EXPECT_EQ(info.version.GetString(), v2->GetString());
EXPECT_EQ(crx_location_, location);
// Remove it so we won't count it ever again.
prefs_->Remove(info.extension_id, NULL);
}
return true;
}
bool OnExternalExtensionUpdateUrlFound(
const ExternalInstallInfoUpdateUrl& info,
bool is_initial_load) override {
++ids_found_;
base::DictionaryValue* pref;
// This tests is to make sure that the provider only notifies us of the
// values we gave it. So if the id we doesn't exist in our internal
// dictionary then something is wrong.
EXPECT_TRUE(prefs_->GetDictionary(info.extension_id, &pref))
<< L"Got back ID (" << info.extension_id.c_str()
<< ") we weren't expecting";
EXPECT_EQ(Manifest::EXTERNAL_PREF_DOWNLOAD, info.download_location);
if (pref) {
EXPECT_TRUE(provider_->HasExtension(info.extension_id));
// External extensions with update URLs do not have versions.
std::unique_ptr<base::Version> v1;
Manifest::Location location1 = Manifest::INVALID_LOCATION;
EXPECT_TRUE(
provider_->GetExtensionDetails(info.extension_id, &location1, &v1));
EXPECT_FALSE(v1.get());
EXPECT_EQ(Manifest::EXTERNAL_PREF_DOWNLOAD, location1);
std::string parsed_install_parameter;
pref->GetString("install_parameter", &parsed_install_parameter);
EXPECT_EQ(parsed_install_parameter, info.install_parameter);
// Remove it so we won't count it again.
prefs_->Remove(info.extension_id, NULL);
}
return true;
}
void OnExternalProviderUpdateComplete(
const ExternalProviderInterface* provider,
const std::vector<ExternalInstallInfoUpdateUrl>& update_url_extensions,
const std::vector<ExternalInstallInfoFile>& file_extensions,
const std::set<std::string>& removed_extensions) override {
ADD_FAILURE() << "MockProviderVisitor does not provide incremental updates,"
" use MockUpdateProviderVisitor instead.";
}
void OnExternalProviderReady(
const ExternalProviderInterface* provider) override {
EXPECT_EQ(provider, provider_.get());
EXPECT_TRUE(provider->IsReady());
}
Profile* profile() { return profile_.get(); }
protected:
std::unique_ptr<ExternalProviderImpl> provider_;
std::unique_ptr<base::DictionaryValue> GetDictionaryFromJSON(
const std::string& json_data) {
// We also parse the file into a dictionary to compare what we get back
// from the provider.
JSONStringValueDeserializer deserializer(json_data);
std::unique_ptr<base::Value> json_value =
deserializer.Deserialize(NULL, NULL);
if (!json_value || !json_value->is_dict()) {
ADD_FAILURE() << "Unable to deserialize json data";
return std::unique_ptr<base::DictionaryValue>();
} else {
return base::DictionaryValue::From(std::move(json_value));
}
}
private:
int ids_found_;
base::FilePath fake_base_path_;
int expected_creation_flags_;
Manifest::Location crx_location_;
std::unique_ptr<base::DictionaryValue> prefs_;
std::unique_ptr<TestingProfile> profile_;
DISALLOW_COPY_AND_ASSIGN(MockProviderVisitor);
};
// Mock provider that can simulate incremental update like
// ExternalRegistryLoader.
class MockUpdateProviderVisitor : public MockProviderVisitor {
public:
// The provider will return |fake_base_path| from
// GetBaseCrxFilePath(). User can test the behavior with
// and without an empty path using this parameter.
explicit MockUpdateProviderVisitor(base::FilePath fake_base_path)
: MockProviderVisitor(fake_base_path) {}
void VisitDueToUpdate(const std::string& json_data) {
update_url_extension_ids_.clear();
file_extension_ids_.clear();
removed_extension_ids_.clear();
std::unique_ptr<base::DictionaryValue> new_prefs =
GetDictionaryFromJSON(json_data);
if (!new_prefs)
return;
provider_->UpdatePrefs(std::move(new_prefs));
}
void OnExternalProviderUpdateComplete(
const ExternalProviderInterface* provider,
const std::vector<ExternalInstallInfoUpdateUrl>& update_url_extensions,
const std::vector<ExternalInstallInfoFile>& file_extensions,
const std::set<std::string>& removed_extensions) override {
for (const auto& extension_info : update_url_extensions)
update_url_extension_ids_.insert(extension_info.extension_id);
EXPECT_EQ(update_url_extension_ids_.size(), update_url_extensions.size());
for (const auto& extension_info : file_extensions)
file_extension_ids_.insert(extension_info.extension_id);
EXPECT_EQ(file_extension_ids_.size(), file_extensions.size());
for (const auto& extension_id : removed_extensions)
removed_extension_ids_.insert(extension_id);
}
size_t GetUpdateURLExtensionCount() {
return update_url_extension_ids_.size();
}
size_t GetFileExtensionCount() { return file_extension_ids_.size(); }
size_t GetRemovedExtensionCount() { return removed_extension_ids_.size(); }
bool HasSeenUpdateWithUpdateUrl(const std::string& extension_id) {
return update_url_extension_ids_.count(extension_id) > 0u;
}
bool HasSeenUpdateWithFile(const std::string& extension_id) {
return file_extension_ids_.count(extension_id) > 0u;
}
bool HasSeenRemoval(const std::string& extension_id) {
return removed_extension_ids_.count(extension_id) > 0u;
}
private:
std::set<std::string> update_url_extension_ids_;
std::set<std::string> file_extension_ids_;
std::set<std::string> removed_extension_ids_;
DISALLOW_COPY_AND_ASSIGN(MockUpdateProviderVisitor);
};
struct MockExtensionRegistryObserver : public ExtensionRegistryObserver {
MockExtensionRegistryObserver() = default;
~MockExtensionRegistryObserver() override = default;
// ExtensionRegistryObserver:
void OnExtensionLoaded(content::BrowserContext* browser_context,
const Extension* extension) override {
last_extension_loaded = extension->id();
}
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionReason reason) override {
last_extension_unloaded = extension->id();
}
void OnExtensionWillBeInstalled(content::BrowserContext* browser_context,
const Extension* extension,
bool is_update,
const std::string& old_name) override {
last_extension_installed = extension->id();
}
void OnExtensionUninstalled(content::BrowserContext* browser_context,
const Extension* extension,
UninstallReason reason) override {
last_extension_uninstalled = extension->id();
}
std::string last_extension_loaded;
std::string last_extension_unloaded;
std::string last_extension_installed;
std::string last_extension_uninstalled;
private:
DISALLOW_COPY_AND_ASSIGN(MockExtensionRegistryObserver);
};
class ExtensionServiceTest : public ExtensionServiceTestWithInstall {
public:
ExtensionServiceTest() = default;
MockExternalProvider* AddMockExternalProvider(Manifest::Location location) {
auto provider = std::make_unique<MockExternalProvider>(service(), location);
MockExternalProvider* provider_ptr = provider.get();
service()->AddProviderForTesting(std::move(provider));
return provider_ptr;
}
// Checks for external extensions and waits for one to complete installing.
void WaitForExternalExtensionInstalled() {
content::WindowedNotificationObserver observer(
NOTIFICATION_CRX_INSTALLER_DONE,
content::NotificationService::AllSources());
service()->CheckForExternalUpdates();
observer.Wait();
}
protected:
void TestExternalProvider(MockExternalProvider* provider,
Manifest::Location location);
// Grants all optional permissions stated in manifest to active permission
// set for extension |id|.
void GrantAllOptionalPermissions(const std::string& id) {
const Extension* extension = service()->GetInstalledExtension(id);
const PermissionSet& all_optional_permissions =
PermissionsParser::GetOptionalPermissions(extension);
permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
profile(), *extension, all_optional_permissions);
}
testing::AssertionResult IsBlocked(const std::string& id) {
std::unique_ptr<ExtensionSet> all_unblocked_extensions =
registry()->GenerateInstalledExtensionsSet(
ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::BLOCKED);
if (all_unblocked_extensions->Contains(id))
return testing::AssertionFailure() << id << " is still unblocked!";
if (!registry()->blocked_extensions().Contains(id))
return testing::AssertionFailure() << id << " is not blocked!";
return testing::AssertionSuccess();
}
// Helper method to test that an extension moves through being blocked and
// unblocked as appropriate for its type.
void AssertExtensionBlocksAndUnblocks(
bool should_block, const std::string extension_id) {
// Assume we start in an unblocked state.
EXPECT_FALSE(IsBlocked(extension_id));
// Block the extensions.
service()->BlockAllExtensions();
content::RunAllTasksUntilIdle();
if (should_block)
ASSERT_TRUE(IsBlocked(extension_id));
else
ASSERT_FALSE(IsBlocked(extension_id));
service()->UnblockAllExtensions();
content::RunAllTasksUntilIdle();
ASSERT_FALSE(IsBlocked(extension_id));
}
bool IsPrefExist(const std::string& extension_id,
const std::string& pref_path) {
const base::DictionaryValue* dict =
profile()->GetPrefs()->GetDictionary(pref_names::kExtensions);
if (dict == NULL) return false;
const base::DictionaryValue* pref = NULL;
if (!dict->GetDictionary(extension_id, &pref)) {
return false;
}
if (pref == NULL) {
return false;
}
bool val;
if (!pref->GetBoolean(pref_path, &val)) {
return false;
}
return true;
}
void SetPref(const std::string& extension_id,
const std::string& pref_path,
std::unique_ptr<base::Value> value,
const std::string& msg) {
DictionaryPrefUpdate update(profile()->GetPrefs(), pref_names::kExtensions);
base::DictionaryValue* dict = update.Get();
ASSERT_TRUE(dict != NULL) << msg;
base::DictionaryValue* pref = NULL;
ASSERT_TRUE(dict->GetDictionary(extension_id, &pref)) << msg;
EXPECT_TRUE(pref != NULL) << msg;
pref->Set(pref_path, std::move(value));
}
void SetPrefInteg(const std::string& extension_id,
const std::string& pref_path,
int value) {
std::string msg = " while setting: ";
msg += extension_id;
msg += " ";
msg += pref_path;
msg += " = ";
msg += base::NumberToString(value);
SetPref(extension_id, pref_path, std::make_unique<base::Value>(value), msg);
}
void SetPrefBool(const std::string& extension_id,
const std::string& pref_path,
bool value) {
std::string msg = " while setting: ";
msg += extension_id + " " + pref_path;
msg += " = ";
msg += (value ? "true" : "false");
SetPref(extension_id, pref_path, std::make_unique<base::Value>(value), msg);
}
void ClearPref(const std::string& extension_id,
const std::string& pref_path) {
std::string msg = " while clearing: ";
msg += extension_id + " " + pref_path;
DictionaryPrefUpdate update(profile()->GetPrefs(), pref_names::kExtensions);
base::DictionaryValue* dict = update.Get();
ASSERT_TRUE(dict != NULL) << msg;
base::DictionaryValue* pref = NULL;
ASSERT_TRUE(dict->GetDictionary(extension_id, &pref)) << msg;
EXPECT_TRUE(pref != NULL) << msg;
pref->Remove(pref_path, NULL);
}
void SetPrefStringSet(const std::string& extension_id,
const std::string& pref_path,
const std::set<std::string>& value) {
std::string msg = " while setting: ";
msg += extension_id + " " + pref_path;
auto list_value = std::make_unique<base::ListValue>();
for (auto iter = value.begin(); iter != value.end(); ++iter)
list_value->AppendString(*iter);
SetPref(extension_id, pref_path, std::move(list_value), msg);
}
void InitPluginService() {
#if BUILDFLAG(ENABLE_PLUGINS)
PluginService::GetInstance()->Init();
#endif
}
void InitializeEmptyExtensionServiceWithTestingPrefs() {
ExtensionServiceTestBase::ExtensionServiceInitParams params =
CreateDefaultInitParams();
params.pref_file = base::FilePath();
InitializeExtensionService(params);
}
ManagementPolicy* GetManagementPolicy() {
return ExtensionSystem::Get(browser_context())->management_policy();
}
ExternalInstallError* GetError(const std::string& extension_id) {
std::vector<ExternalInstallError*> errors =
service_->external_install_manager()->GetErrorsForTesting();
auto found = std::find_if(
errors.begin(), errors.end(),
[&extension_id](const ExternalInstallError* error) {
return error->extension_id() == extension_id;
});
return found == errors.end() ? nullptr : *found;
}
typedef ExtensionManagementPrefUpdater<
sync_preferences::TestingPrefServiceSyncable>
ManagementPrefUpdater;
};
// Receives notifications from a PackExtensionJob, indicating either that
// packing succeeded or that there was some error.
class PackExtensionTestClient : public PackExtensionJob::Client {
public:
PackExtensionTestClient(const base::FilePath& expected_crx_path,
const base::FilePath& expected_private_key_path);
void OnPackSuccess(const base::FilePath& crx_path,
const base::FilePath& private_key_path) override;
void OnPackFailure(const std::string& error_message,
ExtensionCreator::ErrorType type) override;
private:
const base::FilePath expected_crx_path_;
const base::FilePath expected_private_key_path_;
DISALLOW_COPY_AND_ASSIGN(PackExtensionTestClient);
};
PackExtensionTestClient::PackExtensionTestClient(
const base::FilePath& expected_crx_path,
const base::FilePath& expected_private_key_path)
: expected_crx_path_(expected_crx_path),
expected_private_key_path_(expected_private_key_path) {}
// If packing succeeded, we make sure that the package names match our
// expectations.
void PackExtensionTestClient::OnPackSuccess(
const base::FilePath& crx_path,
const base::FilePath& private_key_path) {
// We got the notification and processed it; we don't expect any further tasks
// to be posted to the current thread, so we should stop blocking and continue
// on with the rest of the test.
// This call to |Quit()| matches the call to |Run()| in the
// |PackPunctuatedExtension| test.
base::RunLoop::QuitCurrentWhenIdleDeprecated();
EXPECT_EQ(expected_crx_path_.value(), crx_path.value());
EXPECT_EQ(expected_private_key_path_.value(), private_key_path.value());
ASSERT_TRUE(base::PathExists(private_key_path));
}
// The tests are designed so that we never expect to see a packing error.
void PackExtensionTestClient::OnPackFailure(const std::string& error_message,
ExtensionCreator::ErrorType type) {
if (type == ExtensionCreator::kCRXExists)
FAIL() << "Packing should not fail.";
else
FAIL() << "Existing CRX should have been overwritten.";
}
// Test loading good extensions from the profile directory.
TEST_F(ExtensionServiceTest, LoadAllExtensionsFromDirectorySuccess) {
InitPluginService();
InitializeGoodInstalledExtensionService();
service()->Init();
uint32_t expected_num_extensions = 3u;
ASSERT_EQ(expected_num_extensions, loaded_.size());
EXPECT_EQ(std::string(good0), loaded_[0]->id());
EXPECT_EQ(std::string("My extension 1"),
loaded_[0]->name());
EXPECT_EQ(std::string("The first extension that I made."),
loaded_[0]->description());
EXPECT_EQ(Manifest::INTERNAL, loaded_[0]->location());
EXPECT_TRUE(service()->GetExtensionById(loaded_[0]->id(), false));
EXPECT_EQ(expected_num_extensions, registry()->enabled_extensions().size());
ValidatePrefKeyCount(3);
ValidateIntegerPref(good0, "state", Extension::ENABLED);
ValidateIntegerPref(good0, "location", Manifest::INTERNAL);
ValidateIntegerPref(good1, "state", Extension::ENABLED);
ValidateIntegerPref(good1, "location", Manifest::INTERNAL);
ValidateIntegerPref(good2, "state", Extension::ENABLED);
ValidateIntegerPref(good2, "location", Manifest::INTERNAL);
URLPatternSet expected_patterns;
AddPattern(&expected_patterns, "file:///*");
AddPattern(&expected_patterns, "http://*.google.com/*");
AddPattern(&expected_patterns, "https://*.google.com/*");
const Extension* extension = loaded_[0].get();
const UserScriptList& scripts =
ContentScriptsInfo::GetContentScripts(extension);
ASSERT_EQ(2u, scripts.size());
EXPECT_EQ(expected_patterns, scripts[0]->url_patterns());
EXPECT_EQ(2u, scripts[0]->js_scripts().size());
ExtensionResource resource00(extension->id(),
scripts[0]->js_scripts()[0]->extension_root(),
scripts[0]->js_scripts()[0]->relative_path());
base::FilePath expected_path =
base::MakeAbsoluteFilePath(extension->path().AppendASCII("script1.js"));
EXPECT_TRUE(resource00.ComparePathWithDefault(expected_path));
ExtensionResource resource01(extension->id(),
scripts[0]->js_scripts()[1]->extension_root(),
scripts[0]->js_scripts()[1]->relative_path());
expected_path =
base::MakeAbsoluteFilePath(extension->path().AppendASCII("script2.js"));
EXPECT_TRUE(resource01.ComparePathWithDefault(expected_path));
EXPECT_EQ(1u, scripts[1]->url_patterns().patterns().size());
EXPECT_EQ("http://*.news.com/*",
scripts[1]->url_patterns().begin()->GetAsString());
ExtensionResource resource10(extension->id(),
scripts[1]->js_scripts()[0]->extension_root(),
scripts[1]->js_scripts()[0]->relative_path());
expected_path =
extension->path().AppendASCII("js_files").AppendASCII("script3.js");
expected_path = base::MakeAbsoluteFilePath(expected_path);
EXPECT_TRUE(resource10.ComparePathWithDefault(expected_path));
expected_patterns.ClearPatterns();
AddPattern(&expected_patterns, "http://*.google.com/*");
AddPattern(&expected_patterns, "https://*.google.com/*");
EXPECT_EQ(
expected_patterns,
extension->permissions_data()->active_permissions().explicit_hosts());
EXPECT_EQ(std::string(good1), loaded_[1]->id());
EXPECT_EQ(std::string("My extension 2"), loaded_[1]->name());
EXPECT_EQ(std::string(), loaded_[1]->description());
EXPECT_EQ(loaded_[1]->GetResourceURL("background.html"),
BackgroundInfo::GetBackgroundURL(loaded_[1].get()));
EXPECT_TRUE(ContentScriptsInfo::GetContentScripts(loaded_[1].get()).empty());
EXPECT_EQ(Manifest::INTERNAL, loaded_[1]->location());
int index = expected_num_extensions - 1;
EXPECT_EQ(std::string(good2), loaded_[index]->id());
EXPECT_EQ(std::string("My extension 3"), loaded_[index]->name());
EXPECT_EQ(std::string(), loaded_[index]->description());
EXPECT_TRUE(
ContentScriptsInfo::GetContentScripts(loaded_[index].get()).empty());
EXPECT_EQ(Manifest::INTERNAL, loaded_[index]->location());
}
// Test loading bad extensions from the profile directory.
TEST_F(ExtensionServiceTest, LoadAllExtensionsFromDirectoryFail) {
// Initialize the test dir with a bad Preferences/extensions.
base::FilePath source_install_dir =
data_dir().AppendASCII("bad").AppendASCII("Extensions");
base::FilePath pref_path =
source_install_dir.DirName().Append(chrome::kPreferencesFilename);
InitializeInstalledExtensionService(pref_path, source_install_dir);
service()->Init();
ASSERT_EQ(4u, GetErrors().size());
ASSERT_EQ(0u, loaded_.size());
EXPECT_TRUE(base::MatchPattern(
base::UTF16ToUTF8(GetErrors()[0]),
l10n_util::GetStringUTF8(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE) + " *. " +
manifest_errors::kManifestUnreadable))
<< base::UTF16ToUTF8(GetErrors()[0]);
EXPECT_TRUE(base::MatchPattern(
base::UTF16ToUTF8(GetErrors()[1]),
l10n_util::GetStringUTF8(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE) + " *. " +
manifest_errors::kManifestUnreadable))
<< base::UTF16ToUTF8(GetErrors()[1]);
EXPECT_TRUE(base::MatchPattern(
base::UTF16ToUTF8(GetErrors()[2]),
l10n_util::GetStringUTF8(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE) + " *. " +
manifest_errors::kMissingFile))
<< base::UTF16ToUTF8(GetErrors()[2]);
EXPECT_TRUE(base::MatchPattern(
base::UTF16ToUTF8(GetErrors()[3]),
l10n_util::GetStringUTF8(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE) + " *. " +
manifest_errors::kManifestUnreadable))
<< base::UTF16ToUTF8(GetErrors()[3]);
}
// Test various cases for delayed install because of missing imports.
TEST_F(ExtensionServiceTest, PendingImports) {
InitPluginService();
base::FilePath source_install_dir =
data_dir().AppendASCII("pending_updates_with_imports").AppendASCII(
"Extensions");
base::FilePath pref_path =
source_install_dir.DirName().Append(chrome::kPreferencesFilename);
InitializeInstalledExtensionService(pref_path, source_install_dir);
// Verify there are no pending extensions initially.
EXPECT_FALSE(service()->pending_extension_manager()->HasPendingExtensions());
service()->Init();
// Wait for GarbageCollectExtensions task to complete.
content::RunAllTasksUntilIdle();
// These extensions are used by the extensions we test below, they must be
// installed.
EXPECT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
"bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0")));
EXPECT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
"hpiknbiabeeppbpihjehijgoemciehgk/2")));
// Each of these extensions should have been rejected because of dependencies
// that cannot be satisfied.
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
EXPECT_FALSE(
prefs->GetDelayedInstallInfo("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
EXPECT_FALSE(
prefs->GetInstalledExtensionInfo("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
EXPECT_FALSE(
prefs->GetDelayedInstallInfo("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
EXPECT_FALSE(
prefs->GetInstalledExtensionInfo("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
EXPECT_FALSE(
prefs->GetDelayedInstallInfo("cccccccccccccccccccccccccccccccc"));
EXPECT_FALSE(
prefs->GetInstalledExtensionInfo("cccccccccccccccccccccccccccccccc"));
// Make sure the import started for the extension with a dependency.
EXPECT_TRUE(
prefs->GetDelayedInstallInfo("behllobkkfkfnphdnhnkndlbkcpglgmj"));
EXPECT_EQ(ExtensionPrefs::DELAY_REASON_WAIT_FOR_IMPORTS,
prefs->GetDelayedInstallReason("behllobkkfkfnphdnhnkndlbkcpglgmj"));
EXPECT_FALSE(base::PathExists(extensions_install_dir().AppendASCII(
"behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0")));
EXPECT_TRUE(service()->pending_extension_manager()->HasPendingExtensions());
std::string pending_id("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
EXPECT_TRUE(service()->pending_extension_manager()->IsIdPending(pending_id));
// Remove it because we are not testing the pending extension manager's
// ability to download and install extensions.
EXPECT_TRUE(service()->pending_extension_manager()->Remove(pending_id));
}
// Tests that reloading extension with a install delayed due to pending imports
// reloads currently installed extension version, rather than installing the
// delayed install.
TEST_F(ExtensionServiceTest, ReloadExtensionWithPendingImports) {
InitializeEmptyExtensionService();
// Wait for GarbageCollectExtensions task to complete.
content::RunAllTasksUntilIdle();
const base::FilePath base_path =
data_dir()
.AppendASCII("pending_updates_with_imports")
.AppendASCII("updated_with_imports");
const base::FilePath pem_path = base_path.AppendASCII("update.pem");
// Initially installed version - the version with no imports.
const base::FilePath installed_path = base_path.AppendASCII("1.0.0");
// The updated version - has import that is not satisfied (due to the imported
// extension not being installed).
const base::FilePath updated_path = base_path.AppendASCII("2.0.0");
ASSERT_TRUE(base::PathExists(pem_path));
ASSERT_TRUE(base::PathExists(installed_path));
ASSERT_TRUE(base::PathExists(updated_path));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
// Install version 1.
const Extension* extension =
PackAndInstallCRX(installed_path, pem_path, INSTALL_NEW,
Extension::FROM_WEBSTORE, Manifest::Location::INTERNAL);
content::RunAllTasksUntilIdle();
ASSERT_TRUE(extension);
const std::string id = extension->id();
ASSERT_TRUE(registry()->enabled_extensions().Contains(id));
EXPECT_EQ("1.0.0", extension->VersionString());
// No pending extensions at this point.
EXPECT_FALSE(service()->pending_extension_manager()->HasPendingExtensions());
// Update to version 2 that adds an unsatisfied import.
PackCRXAndUpdateExtension(id, updated_path, pem_path, ENABLED);
content::RunAllTasksUntilIdle();
EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
// The extension update should be delayed at this point - the old version
// should still be installed.
EXPECT_EQ("1.0.0", extension->VersionString());
// Make sure the import started for the extension with a dependency.
EXPECT_TRUE(prefs->GetDelayedInstallInfo(id));
EXPECT_EQ(ExtensionPrefs::DELAY_REASON_WAIT_FOR_IMPORTS,
prefs->GetDelayedInstallReason(id));
const std::string pending_id(32, 'e');
EXPECT_TRUE(service()->pending_extension_manager()->IsIdPending(pending_id));
MockExtensionRegistryObserver reload_observer;
registry()->AddObserver(&reload_observer);
// Reload the extension, and verify that the installed version does not
// change.
service()->ReloadExtension(id);
EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
EXPECT_EQ(id, reload_observer.last_extension_loaded);
EXPECT_EQ(id, reload_observer.last_extension_unloaded);
registry()->RemoveObserver(&reload_observer);
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_EQ("1.0.0", extension->VersionString());
// The update should remain delayed, with the import pending.
EXPECT_TRUE(prefs->GetDelayedInstallInfo(id));
EXPECT_EQ(ExtensionPrefs::DELAY_REASON_WAIT_FOR_IMPORTS,
prefs->GetDelayedInstallReason(id));
// Attempt delayed installed - similar to reloading the extension, the update
// should remain delayed.
EXPECT_FALSE(service()->FinishDelayedInstallationIfReady(id, true));
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_EQ("1.0.0", extension->VersionString());
EXPECT_EQ(ExtensionPrefs::DELAY_REASON_WAIT_FOR_IMPORTS,
prefs->GetDelayedInstallReason(id));
EXPECT_TRUE(service()->pending_extension_manager()->IsIdPending(pending_id));
// Remove the pending install because the pending extension manager's
// ability to download and install extensions is not important for this test.
EXPECT_TRUE(service()->pending_extension_manager()->Remove(pending_id));
}
// Tests that installation fails with extensions disabled.
TEST_F(ExtensionServiceTest, InstallExtensionsWithExtensionsDisabled) {
InitializeExtensionServiceWithExtensionsDisabled();
base::FilePath path = data_dir().AppendASCII("good.crx");
InstallCRX(path, INSTALL_FAILED);
}
// Test installing extensions. This test tries to install few extensions using
// crx files. If you need to change those crx files, feel free to repackage
// them, throw away the key used and change the id's above.
TEST_F(ExtensionServiceTest, InstallExtension) {
InitializeEmptyExtensionService();
ValidatePrefKeyCount(0);
// A simple extension that should install without error.
base::FilePath path = data_dir().AppendASCII("good.crx");
InstallCRX(path, INSTALL_NEW);
// TODO(erikkay): verify the contents of the installed extension.
int pref_count = 0;
ValidatePrefKeyCount(++pref_count);
ValidateIntegerPref(good_crx, "state", Extension::ENABLED);
ValidateIntegerPref(good_crx, "location", Manifest::INTERNAL);
// An extension with page actions.
path = data_dir().AppendASCII("page_action.crx");
InstallCRX(path, INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
ValidateIntegerPref(page_action, "state", Extension::ENABLED);
ValidateIntegerPref(page_action, "location", Manifest::INTERNAL);
// Bad signature.
path = data_dir().AppendASCII("bad_signature.crx");
InstallCRX(path, INSTALL_FAILED);
ValidatePrefKeyCount(pref_count);
// 0-length extension file.
path = data_dir().AppendASCII("not_an_extension.crx");
InstallCRX(path, INSTALL_FAILED);
ValidatePrefKeyCount(pref_count);
// Bad magic number.
path = data_dir().AppendASCII("bad_magic.crx");
InstallCRX(path, INSTALL_FAILED);
ValidatePrefKeyCount(pref_count);
// Packed extensions may have folders or files that have underscores.
// This will only cause a warning, rather than a fatal error.
path = data_dir().AppendASCII("bad_underscore.crx");
InstallCRX(path, INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
// A test for an extension with a 2048-bit public key.
path = data_dir().AppendASCII("good2048.crx");
InstallCRX(path, INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
ValidateIntegerPref(good2048, "state", Extension::ENABLED);
ValidateIntegerPref(good2048, "location", Manifest::INTERNAL);
// TODO(erikkay): add more tests for many of the failure cases.
// TODO(erikkay): add tests for upgrade cases.
}
// Test that correct notifications are sent to ExtensionRegistryObserver on
// extension install and uninstall.
TEST_F(ExtensionServiceTest, InstallObserverNotified) {
InitializeEmptyExtensionService();
ExtensionRegistry* registry(ExtensionRegistry::Get(profile()));
MockExtensionRegistryObserver observer;
registry->AddObserver(&observer);
// A simple extension that should install without error.
ASSERT_TRUE(observer.last_extension_installed.empty());
base::FilePath path = data_dir().AppendASCII("good.crx");
InstallCRX(path, INSTALL_NEW);
ASSERT_EQ(good_crx, observer.last_extension_installed);
// Uninstall the extension.
ASSERT_TRUE(observer.last_extension_uninstalled.empty());
UninstallExtension(good_crx);
ASSERT_EQ(good_crx, observer.last_extension_uninstalled);
registry->RemoveObserver(&observer);
}
// Tests that flags passed to OnExternalExtensionFileFound() make it to the
// extension object.
TEST_F(ExtensionServiceTest, InstallingExternalExtensionWithFlags) {
const char kPrefFromBookmark[] = "from_bookmark";
InitializeEmptyExtensionService();
base::FilePath path = data_dir().AppendASCII("good.crx");
// Register and install an external extension.
std::string version_str = "1.0.0.0";
std::unique_ptr<ExternalInstallInfoFile> info = CreateExternalExtension(
good_crx, version_str, path, Manifest::EXTERNAL_PREF,
Extension::FROM_BOOKMARK);
MockExternalProvider* provider =
AddMockExternalProvider(Manifest::EXTERNAL_POLICY_DOWNLOAD);
provider->UpdateOrAddExtension(std::move(info));
WaitForExternalExtensionInstalled();
const Extension* extension = service()->GetExtensionById(good_crx, false);
ASSERT_TRUE(extension);
ASSERT_TRUE(extension->from_bookmark());
ASSERT_TRUE(ValidateBooleanPref(good_crx, kPrefFromBookmark, true));
// Upgrade to version 2.0, the flag should be preserved.
path = data_dir().AppendASCII("good2.crx");
UpdateExtension(good_crx, path, ENABLED);
ASSERT_TRUE(ValidateBooleanPref(good_crx, kPrefFromBookmark, true));
extension = service()->GetExtensionById(good_crx, false);
ASSERT_TRUE(extension);
ASSERT_TRUE(extension->from_bookmark());
}
// Test the handling of Extension::EXTERNAL_EXTENSION_UNINSTALLED
TEST_F(ExtensionServiceTest, UninstallingExternalExtensions) {
InitializeEmptyExtensionService();
base::FilePath path = data_dir().AppendASCII("good.crx");
std::string version_str = "1.0.0.0";
// Install an external extension.
std::unique_ptr<ExternalInstallInfoFile> info =
CreateExternalExtension(good_crx, version_str, path,
Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
MockExternalProvider* provider =
AddMockExternalProvider(Manifest::EXTERNAL_PREF);
provider->UpdateOrAddExtension(std::move(info));
WaitForExternalExtensionInstalled();
ASSERT_TRUE(service()->GetExtensionById(good_crx, false));
// Uninstall it and check that its killbit gets set.
UninstallExtension(good_crx);
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
EXPECT_TRUE(prefs->IsExternalExtensionUninstalled(good_crx));
// Try to re-install it externally. This should fail because of the killbit.
info = CreateExternalExtension(good_crx, version_str, path,
Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
provider->UpdateOrAddExtension(std::move(info));
content::RunAllTasksUntilIdle();
ASSERT_FALSE(service()->GetExtensionById(good_crx, false));
EXPECT_TRUE(prefs->IsExternalExtensionUninstalled(good_crx));
std::string newer_version = "1.0.0.1";
// Repeat the same thing with a newer version of the extension.
path = data_dir().AppendASCII("good2.crx");
info = CreateExternalExtension(good_crx, newer_version, path,
Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
provider->UpdateOrAddExtension(std::move(info));
content::RunAllTasksUntilIdle();
ASSERT_FALSE(service()->GetExtensionById(good_crx, false));
EXPECT_TRUE(prefs->IsExternalExtensionUninstalled(good_crx));
// Try adding the same extension from an external update URL.
ASSERT_FALSE(service()->pending_extension_manager()->AddFromExternalUpdateUrl(
good_crx,
std::string(),
GURL("http:://fake.update/url"),
Manifest::EXTERNAL_PREF_DOWNLOAD,
Extension::NO_FLAGS,
false));
// Installation of the same extension through the policy should be successful.
ASSERT_TRUE(service()->pending_extension_manager()->AddFromExternalUpdateUrl(
good_crx,
std::string(),
GURL("http:://fake.update/url"),
Manifest::EXTERNAL_POLICY_DOWNLOAD,
Extension::NO_FLAGS,
false));
EXPECT_TRUE(service()->pending_extension_manager()->IsIdPending(good_crx));
EXPECT_TRUE(service()->pending_extension_manager()->Remove(good_crx));
ASSERT_FALSE(service()->pending_extension_manager()->IsIdPending(good_crx));
}
// Test that uninstalling an external extension does not crash when
// the extension could not be loaded.
// This extension shown in preferences file requires an experimental permission.
// It could not be loaded without such permission.
TEST_F(ExtensionServiceTest, UninstallingNotLoadedExtension) {
base::FilePath source_install_dir =
data_dir().AppendASCII("good").AppendASCII("Extensions");
// The preference contains an external extension
// that requires 'experimental' permission.
base::FilePath pref_path = source_install_dir
.DirName()
.AppendASCII("PreferencesExperimental");
// Aforementioned extension will not be loaded if
// there is no '--enable-experimental-extension-apis' command line flag.
InitializeInstalledExtensionService(pref_path, source_install_dir);
service()->Init();
// Check and try to uninstall it.
// If we don't check whether the extension is loaded before we uninstall it
// in CheckExternalUninstall, a crash will happen here because we will get or
// dereference a NULL pointer (extension) inside UninstallExtension.
MockExternalProvider provider(NULL, Manifest::EXTERNAL_REGISTRY);
service()->OnExternalProviderReady(&provider);
}
// Test that external extensions with incorrect IDs are not installed.
TEST_F(ExtensionServiceTest, FailOnWrongId) {
InitializeEmptyExtensionService();
base::FilePath path = data_dir().AppendASCII("good.crx");
std::string version_str = "1.0.0.0";
const std::string wrong_id = all_zero;
const std::string correct_id = good_crx;
ASSERT_NE(correct_id, wrong_id);
MockExternalProvider* provider =
AddMockExternalProvider(Manifest::EXTERNAL_PREF);
// Install an external extension with an ID from the external
// source that is not equal to the ID in the extension manifest.
std::unique_ptr<ExternalInstallInfoFile> info =
CreateExternalExtension(wrong_id, version_str, path,
Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
provider->UpdateOrAddExtension(std::move(info));
WaitForExternalExtensionInstalled();
ASSERT_FALSE(service()->GetExtensionById(good_crx, false));
// Try again with the right ID. Expect success.
info = CreateExternalExtension(correct_id, version_str, path,
Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
provider->UpdateOrAddExtension(std::move(info));
WaitForExternalExtensionInstalled();
ASSERT_TRUE(service()->GetExtensionById(good_crx, false));
}
// Test that external extensions with incorrect versions are not installed.
TEST_F(ExtensionServiceTest, FailOnWrongVersion) {
InitializeEmptyExtensionService();
base::FilePath path = data_dir().AppendASCII("good.crx");
MockExternalProvider* provider =
AddMockExternalProvider(Manifest::EXTERNAL_PREF);
// Install an external extension with a version from the external
// source that is not equal to the version in the extension manifest.
std::string wrong_version_str = "1.2.3.4";
std::unique_ptr<ExternalInstallInfoFile> wrong_info =
CreateExternalExtension(good_crx, wrong_version_str, path,
Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
provider->UpdateOrAddExtension(std::move(wrong_info));
WaitForExternalExtensionInstalled();
ASSERT_FALSE(service()->GetExtensionById(good_crx, false));
// Try again with the right version. Expect success.
service()->pending_extension_manager()->Remove(good_crx);
std::unique_ptr<ExternalInstallInfoFile> correct_info =
CreateExternalExtension(good_crx, "1.0.0.0", path,
Manifest::EXTERNAL_PREF, Extension::NO_FLAGS);
provider->UpdateOrAddExtension(std::move(correct_info));
WaitForExternalExtensionInstalled();
ASSERT_TRUE(service()->GetExtensionById(good_crx, false));
}
// Install a user script (they get converted automatically to an extension)
TEST_F(ExtensionServiceTest, InstallUserScript) {
// The details of script conversion are tested elsewhere, this just tests
// integration with ExtensionService.
InitializeEmptyExtensionService();
base::FilePath path = data_dir().AppendASCII("user_script_basic.user.js");
ASSERT_TRUE(base::PathExists(path));
scoped_refptr<CrxInstaller> installer(CrxInstaller::CreateSilent(service()));
installer->set_allow_silent_install(true);
installer->InstallUserScript(
path,
GURL("http://www.aaronboodman.com/scripts/user_script_basic.user.js"));
content::RunAllTasksUntilIdle();
std::vector<base::string16> errors = GetErrors();
EXPECT_TRUE(installed_) << "Nothing was installed.";
EXPECT_FALSE(was_update_) << path.value();
ASSERT_EQ(1u, loaded_.size()) << "Nothing was loaded.";
EXPECT_EQ(0u, errors.size())
<< "There were errors: "
<< base::JoinString(errors, base::ASCIIToUTF16(","));
EXPECT_TRUE(service()->GetExtensionById(loaded_[0]->id(), false))
<< path.value();
installed_ = NULL;
was_update_ = false;
loaded_.clear();
LoadErrorReporter::GetInstance()->ClearErrors();
}
// Extensions don't install during shutdown.
TEST_F(ExtensionServiceTest, InstallExtensionDuringShutdown) {
InitializeEmptyExtensionService();
// Simulate shutdown.
service()->set_browser_terminating_for_test(true);
base::FilePath path = data_dir().AppendASCII("good.crx");
scoped_refptr<CrxInstaller> installer(CrxInstaller::CreateSilent(service()));
installer->set_allow_silent_install(true);
installer->InstallCrx(path);
content::RunAllTasksUntilIdle();
EXPECT_FALSE(installed_) << "Extension installed during shutdown.";
ASSERT_EQ(0u, loaded_.size()) << "Extension loaded during shutdown.";
}
// This tests that the granted permissions preferences are correctly set when
// installing an extension.
TEST_F(ExtensionServiceTest, GrantedPermissions) {
InitializeEmptyExtensionService();
base::FilePath path = data_dir().AppendASCII("permissions");
base::FilePath pem_path = path.AppendASCII("unknown.pem");
path = path.AppendASCII("unknown");
ASSERT_TRUE(base::PathExists(pem_path));
ASSERT_TRUE(base::PathExists(path));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
APIPermissionSet expected_api_perms;
URLPatternSet expected_host_perms;
// Make sure there aren't any granted permissions before the
// extension is installed.
EXPECT_FALSE(prefs->GetGrantedPermissions(permissions_crx).get());
const Extension* extension = PackAndInstallCRX(path, pem_path, INSTALL_NEW);
EXPECT_EQ(0u, GetErrors().size());
ASSERT_EQ(1u, registry()->enabled_extensions().size());
EXPECT_EQ(permissions_crx, extension->id());
// Verify that the valid API permissions have been recognized.
expected_api_perms.insert(APIPermission::kTab);
AddPattern(&expected_host_perms, "http://*.google.com/*");
AddPattern(&expected_host_perms, "https://*.google.com/*");
AddPattern(&expected_host_perms, "http://*.google.com.hk/*");
AddPattern(&expected_host_perms, "http://www.example.com/*");
std::unique_ptr<const PermissionSet> known_perms =
prefs->GetGrantedPermissions(extension->id());
ASSERT_TRUE(known_perms.get());
EXPECT_FALSE(known_perms->IsEmpty());
EXPECT_EQ(expected_api_perms, known_perms->apis());
EXPECT_EQ(expected_host_perms, known_perms->effective_hosts());
}
// This tests that the granted permissions preferences are correctly set when
// updating an extension, and the extension is disabled in case of a permission
// escalation.
TEST_F(ExtensionServiceTest, GrantedPermissionsOnUpdate) {
InitializeEmptyExtensionService();
const base::FilePath base_path = data_dir().AppendASCII("permissions");
const base::FilePath pem_path = base_path.AppendASCII("update.pem");
const base::FilePath path1 = base_path.AppendASCII("update_1");
const base::FilePath path2 = base_path.AppendASCII("update_2");
const base::FilePath path3 = base_path.AppendASCII("update_3");
const base::FilePath path4 = base_path.AppendASCII("update_4");
const base::FilePath path5 = base_path.AppendASCII("update_5");
ASSERT_TRUE(base::PathExists(pem_path));
ASSERT_TRUE(base::PathExists(path1));
ASSERT_TRUE(base::PathExists(path2));
ASSERT_TRUE(base::PathExists(path3));
ASSERT_TRUE(base::PathExists(path4));
ASSERT_TRUE(base::PathExists(path5));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
// Install version 1, which has the kHistory permission.
const Extension* extension = PackAndInstallCRX(path1, pem_path, INSTALL_NEW);
const std::string id = extension->id();
EXPECT_EQ(0u, GetErrors().size());
ASSERT_TRUE(registry()->enabled_extensions().Contains(id));
// Verify that the history permission has been recognized.
APIPermissionSet expected_api_perms;
expected_api_perms.insert(APIPermission::kHistory);
{
std::unique_ptr<const PermissionSet> known_perms =
prefs->GetGrantedPermissions(id);
ASSERT_TRUE(known_perms.get());
EXPECT_EQ(expected_api_perms, known_perms->apis());
}
// Update to version 2 that adds the kTopSites permission, which has a
// separate message, but is implied by kHistory. The extension should remain
// enabled.
PackCRXAndUpdateExtension(id, path2, pem_path, ENABLED);
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
// The extra permission should have been granted automatically.
expected_api_perms.insert(APIPermission::kTopSites);
{
std::unique_ptr<const PermissionSet> known_perms =
prefs->GetGrantedPermissions(id);
ASSERT_TRUE(known_perms.get());
EXPECT_EQ(expected_api_perms, known_perms->apis());
}
// Update to version 3 that adds the kStorage permission, which does not have
// a message. The extension should remain enabled.
PackCRXAndUpdateExtension(id, path3, pem_path, ENABLED);
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
// The extra permission should have been granted automatically.
expected_api_perms.insert(APIPermission::kStorage);
{
std::unique_ptr<const PermissionSet> known_perms =
prefs->GetGrantedPermissions(id);
ASSERT_TRUE(known_perms.get());
EXPECT_EQ(expected_api_perms, known_perms->apis());
}
// Update to version 4 that adds the kNotifications permission, which has a
// message and hence is considered a permission increase. Now the extension
// should get disabled.
PackCRXAndUpdateExtension(id, path4, pem_path, DISABLED);
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_TRUE(registry()->disabled_extensions().Contains(id));
// No new permissions should have been granted.
{
std::unique_ptr<const PermissionSet> known_perms =
prefs->GetGrantedPermissions(id);
ASSERT_TRUE(known_perms.get());
EXPECT_EQ(expected_api_perms, known_perms->apis());
}
}
TEST_F(ExtensionServiceTest, ReenableWithAllPermissionsGranted) {
InitializeEmptyExtensionService();
const base::FilePath base_path = data_dir().AppendASCII("permissions");
const base::FilePath pem_path = base_path.AppendASCII("update.pem");
const base::FilePath path1 = base_path.AppendASCII("update_1");
const base::FilePath path4 = base_path.AppendASCII("update_4");
const base::FilePath path5 = base_path.AppendASCII("update_5");
ASSERT_TRUE(base::PathExists(pem_path));
ASSERT_TRUE(base::PathExists(path1));
ASSERT_TRUE(base::PathExists(path4));
ASSERT_TRUE(base::PathExists(path5));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
// Install version 1, which has the kHistory permission.
const Extension* extension = PackAndInstallCRX(path1, pem_path, INSTALL_NEW);
const std::string id = extension->id();
EXPECT_EQ(0u, GetErrors().size());
ASSERT_TRUE(registry()->enabled_extensions().Contains(id));
// Update to version 4 that adds the kNotifications permission, which has a
// message and hence is considered a permission increase. The extension
// should get disabled due to a permissions increase.
PackCRXAndUpdateExtension(id, path4, pem_path, DISABLED);
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_TRUE(registry()->disabled_extensions().Contains(id));
EXPECT_TRUE(prefs->HasDisableReason(
id, disable_reason::DISABLE_PERMISSIONS_INCREASE));
// Update to version 5 that removes the kNotifications permission again.
// The extension should get re-enabled.
PackCRXAndUpdateExtension(id, path5, pem_path, ENABLED);
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
}
TEST_F(ExtensionServiceTest, ReenableWithAllPermissionsGrantedOnStartup) {
InitializeEmptyExtensionService();
const base::FilePath base_path = data_dir().AppendASCII("permissions");
const base::FilePath pem_path = base_path.AppendASCII("update.pem");
const base::FilePath path1 = base_path.AppendASCII("update_1");
ASSERT_TRUE(base::PathExists(pem_path));
ASSERT_TRUE(base::PathExists(path1));
// Install an extension which has the kHistory permission.
const Extension* extension = PackAndInstallCRX(path1, pem_path, INSTALL_NEW);
const std::string id = extension->id();
EXPECT_EQ(0u, GetErrors().size());
ASSERT_TRUE(registry()->enabled_extensions().Contains(id));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
// Disable the extension due to a supposed permission increase, but retain its
// granted permissions.
service()->DisableExtension(id, disable_reason::DISABLE_PERMISSIONS_INCREASE);
EXPECT_TRUE(registry()->disabled_extensions().Contains(id));
EXPECT_TRUE(prefs->HasDisableReason(
id, disable_reason::DISABLE_PERMISSIONS_INCREASE));
// Simulate a Chrome restart. Since the extension has all required
// permissions, it should get re-enabled.
service()->ReloadExtensionsForTest();
EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
EXPECT_FALSE(prefs->HasDisableReason(
id, disable_reason::DISABLE_PERMISSIONS_INCREASE));
}
TEST_F(ExtensionServiceTest,
DontReenableWithAllPermissionsGrantedButOtherReason) {
InitializeEmptyExtensionService();
const base::FilePath base_path = data_dir().AppendASCII("permissions");
const base::FilePath pem_path = base_path.AppendASCII("update.pem");
const base::FilePath path1 = base_path.AppendASCII("update_1");
const base::FilePath path4 = base_path.AppendASCII("update_4");
const base::FilePath path5 = base_path.AppendASCII("update_5");
ASSERT_TRUE(base::PathExists(pem_path));
ASSERT_TRUE(base::PathExists(path1));
ASSERT_TRUE(base::PathExists(path4));
ASSERT_TRUE(base::PathExists(path5));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
// Install version 1, which has the kHistory permission.
const Extension* extension = PackAndInstallCRX(path1, pem_path, INSTALL_NEW);
const std::string id = extension->id();
EXPECT_EQ(0u, GetErrors().size());
ASSERT_TRUE(registry()->enabled_extensions().Contains(id));
// Disable the extension.
service()->DisableExtension(id, disable_reason::DISABLE_USER_ACTION);
EXPECT_TRUE(registry()->disabled_extensions().Contains(id));
EXPECT_TRUE(prefs->HasDisableReason(id, disable_reason::DISABLE_USER_ACTION));
// Update to version 4 that adds the kNotifications permission, which has a
// message and hence is considered a permission increase. The extension
// should get disabled due to a permissions increase.
PackCRXAndUpdateExtension(id, path4, pem_path, DISABLED);
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_TRUE(registry()->disabled_extensions().Contains(id));
EXPECT_TRUE(prefs->HasDisableReason(
id, disable_reason::DISABLE_PERMISSIONS_INCREASE));
// The USER_ACTION reason should also still be there.
EXPECT_TRUE(prefs->HasDisableReason(id, disable_reason::DISABLE_USER_ACTION));
// Update to version 5 that removes the kNotifications permission again.
// The PERMISSIONS_INCREASE should be removed, but the extension should stay
// disabled since USER_ACTION is still there.
PackCRXAndUpdateExtension(id, path5, pem_path, DISABLED);
extension = service()->GetInstalledExtension(id);
ASSERT_TRUE(extension);
EXPECT_TRUE(registry()->disabled_extensions().Contains(id));
EXPECT_EQ(disable_reason::DISABLE_USER_ACTION, prefs->GetDisableReasons(id));
}
TEST_F(ExtensionServiceTest,
DontReenableWithAllPermissionsGrantedOnStartupButOtherReason) {
InitializeEmptyExtensionService();
const base::FilePath base_path = data_dir().AppendASCII("permissions");
const base::FilePath pem_path = base_path.AppendASCII("update.pem");
const base::FilePath path1 = base_path.AppendASCII("update_1");
ASSERT_TRUE(base::PathExists(pem_path));
ASSERT_TRUE(base::PathExists(path1));
// Install an extension which has the kHistory permission.
const Extension* extension = PackAndInstallCRX(path1, pem_path, INSTALL_NEW);
const std::string id = extension->id();
EXPECT_EQ(0u, GetErrors().size());
ASSERT_TRUE(registry()->enabled_extensions().Contains(id));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
// Disable the extension due to a supposed permission increase, but retain its
// granted permissions.
service()->DisableExtension(id, disable_reason::DISABLE_PERMISSIONS_INCREASE |
disable_reason::DISABLE_USER_ACTION);
EXPECT_TRUE(registry()->disabled_extensions().Contains(id));
EXPECT_TRUE(prefs->HasDisableReason(
id, disable_reason::DISABLE_PERMISSIONS_INCREASE));
// Simulate a Chrome restart. Since the extension has all required
// permissions, the DISABLE_PERMISSIONS_INCREASE should get removed, but it
// should stay disabled due to the remaining DISABLE_USER_ACTION reason.
service()->ReloadExtensionsForTest();
EXPECT_TRUE(registry()->disabled_extensions().Contains(id));
EXPECT_EQ(disable_reason::DISABLE_USER_ACTION, prefs->GetDisableReasons(id));
}
// Tests that installing an extension with a permission adds it to the granted
// permissions, so that if it is later removed and then re-added the extension
// is not disabled.
TEST_F(ExtensionServiceTest,
ReaddingOldPermissionInUpdateDoesntDisableExtension) {
InitializeEmptyExtensionService();
// Borrow a PEM for consistent IDs.
const base::FilePath pem_path =
data_dir().AppendASCII("permissions/update.pem");
ASSERT_TRUE(base::PathExists(pem_path));
constexpr char kManifestTemplate[] =
R"({
"name": "Test",
"description": "Test permissions update flow",
"manifest_version": 2,
"version": "%s",
"permissions": [%s]
})";
// Install version 1, which includes the tabs permission.
TestExtensionDir version1;
version1.WriteManifest(
base::StringPrintf(kManifestTemplate, "1", R"("tabs")"));
const Extension* extension =
PackAndInstallCRX(version1.UnpackedPath(), pem_path, INSTALL_NEW);
ASSERT_TRUE(extension);
const std::string id = extension->id();
EXPECT_EQ(0u, GetErrors().size());
ASSERT_TRUE(registry()->enabled_extensions().Contains(id));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
auto get_granted_permissions = [prefs, id]() {
return prefs->GetGrantedPermissions(id);
};
auto get_active_permissions = [prefs, id]() {
return prefs->GetActivePermissions(id);
};
APIPermissionSet tabs_permission_set;
tabs_permission_set.insert(APIPermission::kTab);
EXPECT_EQ(tabs_permission_set, get_granted_permissions()->apis());
EXPECT_EQ(tabs_permission_set, get_active_permissions()->apis());
// Version 2 removes the tabs permission. The tabs permission should be
// gone from the active permissions, but retained in the granted permissions.
TestExtensionDir version2;
version2.WriteManifest(base::StringPrintf(kManifestTemplate, "2", ""));
PackCRXAndUpdateExtension(id, version2.UnpackedPath(), pem_path, ENABLED);
EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
EXPECT_EQ(tabs_permission_set, get_granted_permissions()->apis());
EXPECT_TRUE(get_active_permissions()->IsEmpty());
// Version 3 re-adds the tabs permission. Even though this is an increase in
// privilege from version 2, it's not from the granted permissions (which
// include the permission from version 1). Therefore, the extension should
// remain enabled.
TestExtensionDir version3;
version3.WriteManifest(
base::StringPrintf(kManifestTemplate, "3", R"("tabs")"));
PackCRXAndUpdateExtension(id, version3.UnpackedPath(), pem_path, ENABLED);
EXPECT_TRUE(registry()->enabled_extensions().Contains(id));
EXPECT_EQ(tabs_permission_set, get_granted_permissions()->apis());
EXPECT_EQ(tabs_permission_set, get_active_permissions()->apis());
}
#if !defined(OS_CHROMEOS)
// This tests that the granted permissions preferences are correctly set for
// default apps.
TEST_F(ExtensionServiceTest, DefaultAppsGrantedPermissions) {
InitializeEmptyExtensionService();
base::FilePath path = data_dir().AppendASCII("permissions");
base::FilePath pem_path = path.AppendASCII("unknown.pem");
path = path.AppendASCII("unknown");
ASSERT_TRUE(base::PathExists(pem_path));
ASSERT_TRUE(base::PathExists(path));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
APIPermissionSet expected_api_perms;
URLPatternSet expected_host_perms;
// Make sure there aren't any granted permissions before the
// extension is installed.
EXPECT_FALSE(prefs->GetGrantedPermissions(permissions_crx).get());
const Extension* extension = PackAndInstallCRX(
path, pem_path, INSTALL_NEW, Extension::WAS_INSTALLED_BY_DEFAULT,
Manifest::Location::INTERNAL);
EXPECT_EQ(0u, GetErrors().size());
ASSERT_EQ(1u, registry()->enabled_extensions().size());
EXPECT_EQ(permissions_crx, extension->id());
// Verify that the valid API permissions have been recognized.
expected_api_perms.insert(APIPermission::kTab);
std::unique_ptr<const PermissionSet> known_perms =
prefs->GetGrantedPermissions(extension->id());
EXPECT_TRUE(known_perms.get());
EXPECT_FALSE(known_perms->IsEmpty());
EXPECT_EQ(expected_api_perms, known_perms->apis());
}
#endif
// Tests that the extension is disabled when permissions are missing from
// the extension's granted permissions preferences. (This simulates updating
// the browser to a version which recognizes more permissions).
TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) {
InitializeEmptyExtensionService();
base::FilePath path =
data_dir().AppendASCII("permissions").AppendASCII("unknown");
ASSERT_TRUE(base::PathExists(path));
const Extension* extension = PackAndInstallCRX(path, INSTALL_NEW);
EXPECT_EQ(0u, GetErrors().size());
EXPECT_EQ(1u, registry()->enabled_extensions().size());
std::string extension_id = extension->id();
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
APIPermissionSet expected_api_permissions;
URLPatternSet expected_host_permissions;
expected_api_permissions.insert(APIPermission::kTab);
AddPattern(&expected_host_permissions, "http://*.google.com/*");
AddPattern(&expected_host_permissions, "https://*.google.com/*");
AddPattern(&expected_host_permissions, "http://*.google.com.hk/*");
AddPattern(&expected_host_permissions, "http://www.example.com/*");
std::set<std::string> host_permissions;
// Test that the extension is disabled when an API permission is missing from
// the extension's granted api permissions preference. (This simulates
// updating the browser to a version which recognizes a new API permission).
SetPref(extension_id, "granted_permissions.api",
std::make_unique<base::ListValue>(), "granted_permissions.api");
service()->ReloadExtensionsForTest();
EXPECT_EQ(1u, registry()->disabled_extensions().size());
extension = registry()->disabled_extensions().begin()->get();
ASSERT_TRUE(prefs->IsExtensionDisabled(extension_id));
ASSERT_FALSE(service()->IsExtensionEnabled(extension_id));
ASSERT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id));
// Now grant and re-enable the extension, making sure the prefs are updated.
service()->GrantPermissionsAndEnableExtension(extension);
ASSERT_FALSE(prefs->IsExtensionDisabled(extension_id));
ASSERT_TRUE(service()->IsExtensionEnabled(extension_id));
ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id));
std::unique_ptr<const PermissionSet> current_perms =
prefs->GetGrantedPermissions(extension_id);
ASSERT_TRUE(current_perms.get());
ASSERT_FALSE(current_perms->IsEmpty());
ASSERT_EQ(expected_api_permissions, current_perms->apis());
ASSERT_EQ(expected_host_permissions, current_perms->effective_hosts());
// Tests that the extension is disabled when a host permission is missing from
// the extension's granted host permissions preference. (This simulates
// updating the browser to a version which recognizes additional host
// permissions).
host_permissions.clear();
current_perms = NULL;
host_permissions.insert("http://*.google.com/*");
host_permissions.insert("https://*.google.com/*");
host_permissions.insert("http://*.google.com.hk/*");
auto api_permissions = std::make_unique<base::ListValue>();
api_permissions->AppendString("tabs");
SetPref(extension_id, "granted_permissions.api", std::move(api_permissions),
"granted_permissions.api");
SetPrefStringSet(
extension_id, "granted_permissions.scriptable_host", host_permissions);
service()->ReloadExtensionsForTest();
EXPECT_EQ(1u, registry()->disabled_extensions().size());
extension = registry()->disabled_extensions().begin()->get();
ASSERT_TRUE(prefs->IsExtensionDisabled(extension_id));
ASSERT_FALSE(service()->IsExtensionEnabled(extension_id));
ASSERT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id));
// Now grant and re-enable the extension, making sure the prefs are updated.
service()->GrantPermissionsAndEnableExtension(extension);
ASSERT_TRUE(service()->IsExtensionEnabled(extension_id));
ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id));
current_perms = prefs->GetGrantedPermissions(extension_id);
ASSERT_TRUE(current_perms.get());
ASSERT_FALSE(current_perms->IsEmpty());
ASSERT_EQ(expected_api_permissions, current_perms->apis());
ASSERT_EQ(expected_host_permissions, current_perms->effective_hosts());
}
// Test Packaging and installing an extension.
TEST_F(ExtensionServiceTest, PackExtension) {
InitializeEmptyExtensionService();
base::FilePath input_directory =
data_dir()
.AppendASCII("good")
.AppendASCII("Extensions")
.AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
.AppendASCII("1.0.0.0");
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath output_directory = temp_dir.GetPath();
base::FilePath crx_path(output_directory.AppendASCII("ex1.crx"));
base::FilePath privkey_path(output_directory.AppendASCII("privkey.pem"));
std::unique_ptr<ExtensionCreator> creator(new ExtensionCreator());
ASSERT_TRUE(creator->Run(input_directory, crx_path, base::FilePath(),
privkey_path, ExtensionCreator::kNoRunFlags));
ASSERT_TRUE(base::PathExists(crx_path));
ASSERT_TRUE(base::PathExists(privkey_path));
// Repeat the run with the pem file gone, and no special flags
// Should refuse to overwrite the existing crx.
base::DeleteFile(privkey_path, false);
ASSERT_FALSE(creator->Run(input_directory, crx_path, base::FilePath(),
privkey_path, ExtensionCreator::kNoRunFlags));
// OK, now try it with a flag to overwrite existing crx. Should work.
ASSERT_TRUE(creator->Run(input_directory, crx_path, base::FilePath(),
privkey_path, ExtensionCreator::kOverwriteCRX));
// Repeat the run allowing existing crx, but the existing pem is still
// an error. Should fail.
ASSERT_FALSE(creator->Run(input_directory, crx_path, base::FilePath(),
privkey_path, ExtensionCreator::kOverwriteCRX));
ASSERT_TRUE(base::PathExists(privkey_path));
InstallCRX(crx_path, INSTALL_NEW);
// Try packing with invalid paths.
creator.reset(new ExtensionCreator());
ASSERT_FALSE(
creator->Run(base::FilePath(), base::FilePath(), base::FilePath(),
base::FilePath(), ExtensionCreator::kOverwriteCRX));
// Try packing an empty directory. Should fail because an empty directory is
// not a valid extension.
base::ScopedTempDir temp_dir2;
ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
creator.reset(new ExtensionCreator());
ASSERT_FALSE(creator->Run(temp_dir2.GetPath(), crx_path, privkey_path,
base::FilePath(), ExtensionCreator::kOverwriteCRX));
// Try packing with an invalid manifest.
std::string invalid_manifest_content = "I am not a manifest.";
ASSERT_EQ(static_cast<int>(invalid_manifest_content.size()),
base::WriteFile(temp_dir2.GetPath().Append(kManifestFilename),
invalid_manifest_content.c_str(),
invalid_manifest_content.size()));
creator.reset(new ExtensionCreator());
ASSERT_FALSE(creator->Run(temp_dir2.GetPath(), crx_path, privkey_path,
base::FilePath(), ExtensionCreator::kOverwriteCRX));
// Try packing with a private key that is a valid key, but invalid for the
// extension.
base::FilePath bad_private_key_dir =
data_dir().AppendASCII("bad_private_key");
crx_path = output_directory.AppendASCII("bad_private_key.crx");
privkey_path = data_dir().AppendASCII("bad_private_key.pem");
ASSERT_FALSE(creator->Run(bad_private_key_dir, crx_path, base::FilePath(),
privkey_path, ExtensionCreator::kOverwriteCRX));
}
// Test Packaging and installing an extension whose name contains punctuation.
TEST_F(ExtensionServiceTest, PackPunctuatedExtension) {
InitializeEmptyExtensionService();
base::FilePath input_directory = data_dir()
.AppendASCII("good")
.AppendASCII("Extensions")
.AppendASCII(good0)
.AppendASCII("1.0.0.0");
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// Extension names containing punctuation, and the expected names for the
// packed extensions.
const base::FilePath punctuated_names[] = {
base::FilePath(FILE_PATH_LITERAL("this.extensions.name.has.periods")),
base::FilePath(FILE_PATH_LITERAL(".thisextensionsnamestartswithaperiod")),
base::FilePath(FILE_PATH_LITERAL("thisextensionhasaslashinitsname/")).
NormalizePathSeparators(),
};
const base::FilePath expected_crx_names[] = {
base::FilePath(FILE_PATH_LITERAL("this.extensions.name.has.periods.crx")),
base::FilePath(
FILE_PATH_LITERAL(".thisextensionsnamestartswithaperiod.crx")),
base::FilePath(FILE_PATH_LITERAL("thisextensionhasaslashinitsname.crx")),
};
const base::FilePath expected_private_key_names[] = {
base::FilePath(FILE_PATH_LITERAL("this.extensions.name.has.periods.pem")),
base::FilePath(
FILE_PATH_LITERAL(".thisextensionsnamestartswithaperiod.pem")),
base::FilePath(FILE_PATH_LITERAL("thisextensionhasaslashinitsname.pem")),
};
for (size_t i = 0; i < base::size(punctuated_names); ++i) {
SCOPED_TRACE(punctuated_names[i].value().c_str());
base::FilePath output_dir = temp_dir.GetPath().Append(punctuated_names[i]);
// Copy the extension into the output directory, as PackExtensionJob doesn't
// let us choose where to output the packed extension.
ASSERT_TRUE(base::CopyDirectory(input_directory, output_dir, true));
base::FilePath expected_crx_path =
temp_dir.GetPath().Append(expected_crx_names[i]);
base::FilePath expected_private_key_path =
temp_dir.GetPath().Append(expected_private_key_names[i]);
PackExtensionTestClient pack_client(expected_crx_path,
expected_private_key_path);
{
PackExtensionJob packer(&pack_client, output_dir, base::FilePath(),
ExtensionCreator::kOverwriteCRX);
packer.Start();
// The packer will post a notification task to the current thread's
// message loop when it is finished. We manually run the loop here so
// that we block and catch the notification; otherwise, the process would
// exit.
// This call to |Run()| is matched by a call to |Quit()| in the
// |PackExtensionTestClient|'s notification handling code.
base::RunLoop().Run();
}
if (HasFatalFailure())
return;
InstallCRX(expected_crx_path, INSTALL_NEW);
}
}
TEST_F(ExtensionServiceTest, PackExtensionContainingKeyFails) {
InitializeEmptyExtensionService();
base::ScopedTempDir extension_temp_dir;
ASSERT_TRUE(extension_temp_dir.CreateUniqueTempDir());
base::FilePath input_directory =
extension_temp_dir.GetPath().AppendASCII("ext");
ASSERT_TRUE(
base::CopyDirectory(data_dir()
.AppendASCII("good")
.AppendASCII("Extensions")
.AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
.AppendASCII("1.0.0.0"),
input_directory,
/*recursive=*/true));
base::ScopedTempDir output_temp_dir;
ASSERT_TRUE(output_temp_dir.CreateUniqueTempDir());
base::FilePath output_directory = output_temp_dir.GetPath();
base::FilePath crx_path(output_directory.AppendASCII("ex1.crx"));
base::FilePath privkey_path(output_directory.AppendASCII("privkey.pem"));
// Pack the extension once to get a private key.
std::unique_ptr<ExtensionCreator> creator(new ExtensionCreator());
ASSERT_TRUE(creator->Run(input_directory, crx_path, base::FilePath(),
privkey_path, ExtensionCreator::kNoRunFlags))
<< creator->error_message();
ASSERT_TRUE(base::PathExists(crx_path));
ASSERT_TRUE(base::PathExists(privkey_path));
base::DeleteFile(crx_path, false);
// Move the pem file into the extension.
base::Move(privkey_path,
input_directory.AppendASCII("privkey.pem"));
// This pack should fail because of the contained private key.
EXPECT_FALSE(creator->Run(input_directory, crx_path, base::FilePath(),
privkey_path, ExtensionCreator::kNoRunFlags));
EXPECT_THAT(creator->error_message(),
testing::ContainsRegex(
"extension includes the key file.*privkey.pem"));
}
// Test Packaging and installing an extension using an openssl generated key.
// The openssl is generated with the following:
// > openssl genrsa -out privkey.pem 1024
// > openssl pkcs8 -topk8 -nocrypt -in privkey.pem -out privkey_asn1.pem
// The privkey.pem is a PrivateKey, and the pcks8 -topk8 creates a
// PrivateKeyInfo ASN.1 structure, we our RSAPrivateKey expects.
TEST_F(ExtensionServiceTest, PackExtensionOpenSSLKey) {
InitializeEmptyExtensionService();
base::FilePath input_directory =
data_dir()
.AppendASCII("good")
.AppendASCII("Extensions")
.AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
.AppendASCII("1.0.0.0");
base::FilePath privkey_path(
data_dir().AppendASCII("openssl_privkey_asn1.pem"));
ASSERT_TRUE(base::PathExists(privkey_path));
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath output_directory = temp_dir.GetPath();
base::FilePath crx_path(output_directory.AppendASCII("ex1.crx"));
std::unique_ptr<ExtensionCreator> creator(new ExtensionCreator());
ASSERT_TRUE(creator->Run(input_directory, crx_path, privkey_path,
base::FilePath(), ExtensionCreator::kOverwriteCRX));
InstallCRX(crx_path, INSTALL_NEW);
}
TEST_F(ExtensionServiceTest, TestInstallThemeWithExtensionsDisabled) {
// Themes can be installed, even when extensions are disabled.
InitializeExtensionServiceWithExtensionsDisabled();
base::FilePath path = data_dir().AppendASCII("theme.crx");
InstallCRX(path, INSTALL_NEW);
ValidatePrefKeyCount(1);
ValidateIntegerPref(theme_crx, "state", Extension::ENABLED);
ValidateIntegerPref(theme_crx, "location", Manifest::INTERNAL);
}
#if defined(THREAD_SANITIZER)
// Flaky under Tsan. http://crbug.com/377702
#define MAYBE_InstallTheme DISABLED_InstallTheme
#else
#define MAYBE_InstallTheme InstallTheme
#endif
TEST_F(ExtensionServiceTest, MAYBE_InstallTheme) {
InitializeEmptyExtensionService();
service()->Init();
// A theme.
base::FilePath path = data_dir().AppendASCII("theme.crx");
InstallCRX(path, INSTALL_NEW);
int pref_count = 0;
ValidatePrefKeyCount(++pref_count);
ValidateIntegerPref(theme_crx, "state", Extension::ENABLED);
ValidateIntegerPref(theme_crx, "location", Manifest::INTERNAL);
path = data_dir().AppendASCII("theme2.crx");
InstallCRX(path, INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
ValidateIntegerPref(theme2_crx, "state", Extension::ENABLED);
ValidateIntegerPref(theme2_crx, "location", Manifest::INTERNAL);
// A theme with extension elements. Themes cannot have extension elements,
// so any such elements (like content scripts) should be ignored.
{
path = data_dir().AppendASCII("theme_with_extension.crx");
const Extension* extension = InstallCRX(path, INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
ASSERT_TRUE(extension);
EXPECT_TRUE(extension->is_theme());
EXPECT_TRUE(ContentScriptsInfo::GetContentScripts(extension).empty());
}
// A theme with image resources missing (misspelt path).
path = data_dir().AppendASCII("theme_missing_image.crx");
InstallCRX(path, INSTALL_FAILED);
ValidatePrefKeyCount(pref_count);
}
TEST_F(ExtensionServiceTest, LoadLocalizedTheme) {
// Load.
InitializeEmptyExtensionService();
service()->Init();
base::FilePath extension_path = data_dir().AppendASCII("theme_i18n");
UnpackedInstaller::Create(service())->Load(extension_path);
content::RunAllTasksUntilIdle();
EXPECT_EQ(0u, GetErrors().size());
ASSERT_EQ(1u, loaded_.size());
EXPECT_EQ(1u, registry()->enabled_extensions().size());
const Extension* theme = registry()->enabled_extensions().begin()->get();
EXPECT_EQ("name", theme->name());
EXPECT_EQ("description", theme->description());
// Cleanup the "Cached Theme.pak" file. Ideally, this would be installed in a
// temporary directory, but it automatically installs to the extension's
// directory, and we don't want to copy the whole extension for a unittest.
base::FilePath theme_file = extension_path.Append(chrome::kThemePackFilename);
ASSERT_TRUE(base::PathExists(theme_file));
ASSERT_TRUE(base::DeleteFile(theme_file, false)); // Not recursive.
}
#if defined(OS_POSIX)
TEST_F(ExtensionServiceTest, UnpackedExtensionMayContainSymlinkedFiles) {
base::FilePath source_data_dir =
data_dir().AppendASCII("unpacked").AppendASCII("symlinks_allowed");
// Paths to test data files.
base::FilePath source_manifest = source_data_dir.AppendASCII("manifest.json");
ASSERT_TRUE(base::PathExists(source_manifest));
base::FilePath source_icon = source_data_dir.AppendASCII("icon.png");
ASSERT_TRUE(base::PathExists(source_icon));
// Set up the temporary extension directory.
base::ScopedTempDir temp;
ASSERT_TRUE(temp.CreateUniqueTempDir());
base::FilePath extension_path = temp.GetPath();
base::FilePath manifest = extension_path.Append(kManifestFilename);
base::FilePath icon_symlink = extension_path.AppendASCII("icon.png");
base::CopyFile(source_manifest, manifest);
base::CreateSymbolicLink(source_icon, icon_symlink);
// Load extension.
InitializeEmptyExtensionService();
UnpackedInstaller::Create(service())->Load(extension_path);
content::RunAllTasksUntilIdle();
EXPECT_TRUE(GetErrors().empty());
ASSERT_EQ(1u, loaded_.size());
EXPECT_EQ(1u, registry()->enabled_extensions().size());
}
#endif
// Tests than an unpacked extension with an empty kMetadataFolder loads
// successfully.
TEST_F(ExtensionServiceTest, UnpackedExtensionWithEmptyMetadataFolder) {
InitializeEmptyExtensionService();
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath extension_dir = base::MakeAbsoluteFilePath(temp_dir.GetPath());
base::FilePath metadata_dir = extension_dir.Append(kMetadataFolder);
PersistExtensionWithPaths(extension_dir, {metadata_dir}, {});
EXPECT_TRUE(base::DirectoryExists(metadata_dir));
UnpackedInstaller::Create(service())->Load(extension_dir);
content::RunAllTasksUntilIdle();
EXPECT_EQ(0u, GetErrors().size());
EXPECT_EQ(1u, registry()->enabled_extensions().size());
// The kMetadataFolder should have been deleted since it did not contain
// any non-reserved filenames.
EXPECT_FALSE(base::DirectoryExists(metadata_dir));
}
// Tests that an unpacked extension with only reserved filenames in the
// kMetadataFolder loads successfully.
TEST_F(ExtensionServiceTest, UnpackedExtensionWithReservedMetadataFiles) {
InitializeEmptyExtensionService();
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath extension_dir = base::MakeAbsoluteFilePath(temp_dir.GetPath());
base::FilePath metadata_dir = extension_dir.Append(kMetadataFolder);
PersistExtensionWithPaths(
extension_dir, {metadata_dir},
file_util::GetReservedMetadataFilePaths(extension_dir));
EXPECT_TRUE(base::DirectoryExists(metadata_dir));
UnpackedInstaller::Create(service())->Load(extension_dir);
content::RunAllTasksUntilIdle();
EXPECT_EQ(0u, GetErrors().size());
EXPECT_EQ(1u, registry()->enabled_extensions().size());
// The kMetadataFolder should have been deleted since it did not contain
// any non-reserved filenames.
EXPECT_FALSE(base::DirectoryExists(metadata_dir));
}
// Tests that an unpacked extension with non-reserved files in the
// kMetadataFolder fails to load.
TEST_F(ExtensionServiceTest, UnpackedExtensionWithUserMetadataFiles) {
InitializeEmptyExtensionService();
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath extension_dir = base::MakeAbsoluteFilePath(temp_dir.GetPath());
base::FilePath metadata_dir = extension_dir.Append(kMetadataFolder);
base::FilePath non_reserved_file =
metadata_dir.Append(FILE_PATH_LITERAL("a.txt"));
PersistExtensionWithPaths(
extension_dir, {metadata_dir},
{file_util::GetVerifiedContentsPath(extension_dir), non_reserved_file});
EXPECT_TRUE(base::PathExists(non_reserved_file));
UnpackedInstaller::Create(service())->Load(extension_dir);
content::RunAllTasksUntilIdle();
ASSERT_EQ(1u, GetErrors().size());
// Format expected error string.
std::string expected("Failed to load extension from: ");
expected.append(extension_dir.MaybeAsASCII())
.append(
". Cannot load extension with file or directory name _metadata. "
"Filenames starting with \"_\" are reserved for use by the system.");
EXPECT_EQ(base::UTF8ToUTF16(expected), GetErrors()[0]);
EXPECT_EQ(0u, registry()->enabled_extensions().size());
// Non-reserved filepaths inside the kMetadataFolder should not have been
// deleted.
EXPECT_TRUE(base::PathExists(non_reserved_file));
}
// Tests than an unpacked extension with an empty kMetadataFolder and a folder
// beginning with "_" fails to load.
TEST_F(ExtensionServiceTest,
UnpackedExtensionWithEmptyMetadataAndUnderscoreFolders) {
InitializeEmptyExtensionService();
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath extension_dir = base::MakeAbsoluteFilePath(temp_dir.GetPath());
base::FilePath metadata_dir = extension_dir.Append(kMetadataFolder);
PersistExtensionWithPaths(
extension_dir,
{metadata_dir, extension_dir.Append(FILE_PATH_LITERAL("_badfolder"))},
{});
UnpackedInstaller::Create(service())->Load(extension_dir);
content::RunAllTasksUntilIdle();
EXPECT_EQ(1u, GetErrors().size());
// Format expected error string.
std::string expected("Failed to load extension from: ");
expected.append(extension_dir.MaybeAsASCII())
.append(
". Cannot load extension with file or directory name _badfolder. "
"Filenames starting with \"_\" are reserved for use by the system.");
EXPECT_EQ(base::UTF8ToUTF16(expected), GetErrors()[0]);
EXPECT_EQ(0u, registry()->enabled_extensions().size());
// The kMetadataFolder should have been deleted since it did not contain any
// non-reserved filenames.
EXPECT_FALSE(base::DirectoryExists(metadata_dir));
}
// Tests that an unpacked extension with an arbitrary folder beginning with an
// underscore can't load.
TEST_F(ExtensionServiceTest, UnpackedExtensionMayNotHaveUnderscore) {
InitializeEmptyExtensionService();
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath extension_dir = base::MakeAbsoluteFilePath(temp_dir.GetPath());
base::FilePath underscore_folder =
extension_dir.Append(FILE_PATH_LITERAL("_badfolder"));
PersistExtensionWithPaths(
extension_dir, {underscore_folder},
{underscore_folder.Append(FILE_PATH_LITERAL("a.js"))});
EXPECT_TRUE(base::DirectoryExists(underscore_folder));
UnpackedInstaller::Create(service())->Load(extension_dir);
content::RunAllTasksUntilIdle();
EXPECT_EQ(1u, GetErrors().size());
// Format expected error string.
std::string expected("Failed to load extension from: ");
expected.append(extension_dir.MaybeAsASCII())
.append(
". Cannot load extension with file or directory name _badfolder. "
"Filenames starting with \"_\" are reserved for use by the system.");
EXPECT_EQ(base::UTF8ToUTF16(expected), GetErrors()[0]);
EXPECT_EQ(0u, registry()->enabled_extensions().size());
}
TEST_F(ExtensionServiceTest, InstallLocalizedTheme) {
InitializeEmptyExtensionService();
service()->Init();
base::FilePath theme_path = data_dir().AppendASCII("theme_i18n");
const Extension* theme = PackAndInstallCRX(theme_path, INSTALL_NEW);
EXPECT_EQ(0u, GetErrors().size());
EXPECT_EQ(1u, registry()->enabled_extensions().size());
EXPECT_EQ("name", theme->name());
EXPECT_EQ("description", theme->description());
}
TEST_F(ExtensionServiceTest, InstallApps) {
InitializeEmptyExtensionService();
// An empty app.
const Extension* app =
PackAndInstallCRX(data_dir().AppendASCII("app1"), INSTALL_NEW);
int pref_count = 0;
ValidatePrefKeyCount(++pref_count);
ASSERT_EQ(1u, registry()->enabled_extensions().size());
ValidateIntegerPref(app->id(), "state", Extension::ENABLED);
ValidateIntegerPref(app->id(), "location", Manifest::INTERNAL);
// Another app with non-overlapping extent. Should succeed.
PackAndInstallCRX(data_dir().AppendASCII("app2"), INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
// A third app whose extent overlaps the first. Should fail.
PackAndInstallCRX(data_dir().AppendASCII("app3"), INSTALL_FAILED);
ValidatePrefKeyCount(pref_count);
}
// Tests that file access is OFF by default.
TEST_F(ExtensionServiceTest, DefaultFileAccess) {
InitializeEmptyExtensionService();
const Extension* extension = PackAndInstallCRX(
data_dir().AppendASCII("permissions").AppendASCII("files"), INSTALL_NEW);
EXPECT_EQ(0u, GetErrors().size());
EXPECT_EQ(1u, registry()->enabled_extensions().size());
EXPECT_FALSE(
ExtensionPrefs::Get(profile())->AllowFileAccess(extension->id()));
}
TEST_F(ExtensionServiceTest, UpdateApps) {
InitializeEmptyExtensionService();
base::FilePath extensions_path = data_dir().AppendASCII("app_update");
// First install v1 of a hosted app.
const Extension* extension =
InstallCRX(extensions_path.AppendASCII("v1.crx"), INSTALL_NEW);
ASSERT_EQ(1u, registry()->enabled_extensions().size());
std::string id = extension->id();
ASSERT_EQ(std::string("1"), extension->version().GetString());
// Now try updating to v2.
UpdateExtension(id,
extensions_path.AppendASCII("v2.crx"),
ENABLED);
ASSERT_EQ(std::string("2"),
service()->GetExtensionById(id, false)->version().GetString());
}
// Verifies that the NTP page and launch ordinals are kept when updating apps.
TEST_F(ExtensionServiceTest, UpdateAppsRetainOrdinals) {
InitializeEmptyExtensionService();
AppSorting* sorting = ExtensionSystem::Get(profile())->app_sorting();
base::FilePath extensions_path = data_dir().AppendASCII("app_update");
// First install v1 of a hosted app.
const Extension* extension =
InstallCRX(extensions_path.AppendASCII("v1.crx"), INSTALL_NEW);
ASSERT_EQ(1u, registry()->enabled_extensions().size());
std::string id = extension->id();
ASSERT_EQ(std::string("1"), extension->version().GetString());
// Modify the ordinals so we can distinguish them from the defaults.
syncer::StringOrdinal new_page_ordinal =
sorting->GetPageOrdinal(id).CreateAfter();
syncer::StringOrdinal new_launch_ordinal =
sorting->GetAppLaunchOrdinal(id).CreateBefore();
sorting->SetPageOrdinal(id, new_page_ordinal);
sorting->SetAppLaunchOrdinal(id, new_launch_ordinal);
// Now try updating to v2.
UpdateExtension(id, extensions_path.AppendASCII("v2.crx"), ENABLED);
ASSERT_EQ(std::string("2"),
service()->GetExtensionById(id, false)->version().GetString());
// Verify that the ordinals match.
ASSERT_TRUE(new_page_ordinal.Equals(sorting->GetPageOrdinal(id)));
ASSERT_TRUE(new_launch_ordinal.Equals(sorting->GetAppLaunchOrdinal(id)));
}
// Ensures that the CWS has properly initialized ordinals.
TEST_F(ExtensionServiceTest, EnsureCWSOrdinalsInitialized) {
InitializeEmptyExtensionService();
service()->component_loader()->Add(
IDR_WEBSTORE_MANIFEST, base::FilePath(FILE_PATH_LITERAL("web_store")));
service()->Init();
AppSorting* sorting = ExtensionSystem::Get(profile())->app_sorting();
EXPECT_TRUE(sorting->GetPageOrdinal(kWebStoreAppId).IsValid());
EXPECT_TRUE(sorting->GetAppLaunchOrdinal(kWebStoreAppId).IsValid());
}
TEST_F(ExtensionServiceTest, InstallAppsWithUnlimitedStorage) {
InitializeEmptyExtensionService();
EXPECT_TRUE(registry()->enabled_extensions().is_empty());
int pref_count = 0;
// Install app1 with unlimited storage.
const Extension* extension =
PackAndInstallCRX(data_dir().AppendASCII("app1"), INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
ASSERT_EQ(1u, registry()->enabled_extensions().size());
const std::string id1 = extension->id();
EXPECT_TRUE(extension->permissions_data()->HasAPIPermission(
APIPermission::kUnlimitedStorage));
EXPECT_TRUE(extension->web_extent().MatchesURL(
AppLaunchInfo::GetFullLaunchURL(extension)));
const GURL origin1(AppLaunchInfo::GetFullLaunchURL(extension).GetOrigin());
EXPECT_TRUE(profile()->GetExtensionSpecialStoragePolicy()->IsStorageUnlimited(
origin1));
// Install app2 from the same origin with unlimited storage.
extension = PackAndInstallCRX(data_dir().AppendASCII("app2"), INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
ASSERT_EQ(2u, registry()->enabled_extensions().size());
const std::string id2 = extension->id();
EXPECT_TRUE(extension->permissions_data()->HasAPIPermission(
APIPermission::kUnlimitedStorage));
EXPECT_TRUE(extension->web_extent().MatchesURL(
AppLaunchInfo::GetFullLaunchURL(extension)));
const GURL origin2(AppLaunchInfo::GetFullLaunchURL(extension).GetOrigin());
EXPECT_EQ(origin1, origin2);
EXPECT_TRUE(profile()->GetExtensionSpecialStoragePolicy()->IsStorageUnlimited(
origin2));
// Uninstall one of them, unlimited storage should still be granted
// to the origin.
UninstallExtension(id1);
EXPECT_EQ(1u, registry()->enabled_extensions().size());
EXPECT_TRUE(profile()->GetExtensionSpecialStoragePolicy()->IsStorageUnlimited(
origin1));
// Uninstall the other, unlimited storage should be revoked.
UninstallExtension(id2);
EXPECT_EQ(0u, registry()->enabled_extensions().size());
EXPECT_FALSE(
profile()->GetExtensionSpecialStoragePolicy()->IsStorageUnlimited(
origin2));
}
TEST_F(ExtensionServiceTest, InstallAppsAndCheckStorageProtection) {
InitializeEmptyExtensionService();
EXPECT_TRUE(registry()->enabled_extensions().is_empty());
int pref_count = 0;
const Extension* extension =
PackAndInstallCRX(data_dir().AppendASCII("app1"), INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
ASSERT_EQ(1u, registry()->enabled_extensions().size());
EXPECT_TRUE(extension->is_app());
const std::string id1 = extension->id();
const GURL origin1(AppLaunchInfo::GetFullLaunchURL(extension).GetOrigin());
EXPECT_TRUE(profile()->GetExtensionSpecialStoragePolicy()->IsStorageProtected(
origin1));
// App 4 has a different origin (maps.google.com).
extension = PackAndInstallCRX(data_dir().AppendASCII("app4"), INSTALL_NEW);
ValidatePrefKeyCount(++pref_count);
ASSERT_EQ(2u, registry()->enabled_extensions().size());
const std::string id2 = extension->id();
const GURL origin2(AppLaunchInfo::GetFullLaunchURL(extension).GetOrigin());
ASSERT_NE(origin1, origin2);
EXPECT_TRUE(profile()->GetExtensionSpecialStoragePolicy()->IsStorageProtected(
origin2));
UninstallExtension(id1);
EXPECT_EQ(1u, registry()->enabled_extensions().size());
UninstallExtension(id2);
EXPECT_TRUE(registry()->enabled_extensions().is_empty());
EXPECT_FALSE(
profile()->GetExtensionSpecialStoragePolicy()->IsStorageProtected(
origin1));
EXPECT_FALSE(
profile()->GetExtensionSpecialStoragePolicy()->IsStorageProtected(
origin2));
}