Fixed extensions import (#29084)

* Fixed crash during import from multiple profiles.
* Fixed state check of the source extension.
This commit is contained in:
Pavel Beloborodov
2025-05-16 19:45:37 +07:00
committed by GitHub
parent a4adfee963
commit 2fa4515000
7 changed files with 225 additions and 3 deletions
+20
View File
@@ -34,4 +34,24 @@ if (!is_android) {
"//testing/gtest",
]
}
source_set("browser_tests") {
testonly = true
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
sources = [ "brave_importer_browsertest.cc" ]
deps = [
"//base",
"//brave/common/importer:importer",
"//chrome/browser/importer",
"//chrome/browser/ui",
"//chrome/common:constants",
"//chrome/common/importer",
"//chrome/test:test_support",
"//components/value_store:test_support",
"//content/test:test_support",
"//testing/gtest",
]
}
}
@@ -51,10 +51,14 @@ void BraveExternalProcessImporterHost::LaunchImportIfReady() {
weak_ptr_factory_.GetWeakPtr()));
return;
}
if (!extensions_import_ready_) {
return;
}
}
#endif
if (!do_not_launch_import_for_testing_) {
CHECK(!client_);
ExternalProcessImporterHost::LaunchImportIfReady();
} else {
NotifyImportEnded();
@@ -93,6 +97,7 @@ void BraveExternalProcessImporterHost::OnExtensionsImportReady(bool ready) {
weak_ptr_factory_.GetWeakPtr()),
IDS_EXTENSIONS_IMPORTER_LOCK_TITLE, IDS_EXTENSIONS_IMPORTER_LOCK_TEXT);
} else {
extensions_import_ready_ = true;
LaunchImportIfReady();
}
}
@@ -55,6 +55,7 @@ class BraveExternalProcessImporterHost : public ExternalProcessImporterHost {
extensions_import::ExtensionImportStatus status);
std::unique_ptr<extensions_import::ExtensionsImporter> extensions_importer_;
bool extensions_import_ready_ = false;
#endif
bool do_not_launch_import_for_testing_ = false;
@@ -0,0 +1,184 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "base/files/file_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/bind.h"
#include "brave/browser/importer/brave_external_process_importer_host.h"
#include "brave/browser/importer/extensions_import_helpers.h"
#include "brave/common/importer/chrome_importer_utils.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/importer/profile_writer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/value_store/test_value_store_factory.h"
#include "components/value_store/value_store.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/extension_registrar.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr char kExtensionId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
class TestObserver : public importer::ImporterProgressObserver {
public:
~TestObserver() override = default;
void ImportStarted() override {}
void ImportItemStarted(importer::ImportItem item) override {}
void ImportItemEnded(importer::ImportItem item) override {}
MOCK_METHOD(void, ImportEnded, (), (override));
};
} // namespace
class BraveImporterBrowserTest : public InProcessBrowserTest {
public:
Profile* CreateProfile() {
ProfileManager* profile_manager = g_browser_process->profile_manager();
base::FilePath profile_path =
profile_manager->GenerateNextProfileDirectoryPath();
return &profiles::testing::CreateProfileSync(profile_manager, profile_path);
}
void NonBlockingDelay(base::TimeDelta delay) {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitWhenIdleClosure(), delay);
run_loop.Run();
}
std::string ReadStore(Profile* profile) {
base::ScopedAllowBlockingForTesting allow_io;
const auto path =
profile->GetPath()
.Append(extensions::kLocalExtensionSettingsDirectoryName)
.AppendASCII(kExtensionId);
if (!base::DirectoryExists(path)) {
return {};
}
auto store_factory =
base::MakeRefCounted<value_store::TestValueStoreFactory>(path);
auto source_store = store_factory->CreateValueStore(
base::FilePath(extensions::kLocalExtensionSettingsDirectoryName),
kExtensionId);
auto setting = source_store->Get();
if (!setting.status().ok()) {
return {};
}
return *setting.PassSettings().FindString("id");
}
std::string ReadIndexedDB(Profile* profile) {
base::ScopedAllowBlockingForTesting allow_io;
const auto indexeddb_path =
profile->GetPath()
.AppendASCII("IndexedDB")
.AppendASCII(base::StrCat(
{"chrome-extension_", kExtensionId, "_indexeddb.test"}))
.AppendASCII("test");
std::string contents;
base::ReadFileToString(indexeddb_path, &contents);
return contents;
}
void AddTestExtension(Profile* profile) {
scoped_refptr<const extensions::Extension> extension(
extensions::ExtensionBuilder("extension")
.AddFlags(extensions::Extension::FROM_WEBSTORE)
.SetID(kExtensionId)
.AddJSON(R"("manifest_version": 2, "version": "1.0.0")")
.SetLocation(extensions::mojom::ManifestLocation::kInternal)
.Build());
extensions::ExtensionRegistrar::Get(profile)->AddExtension(extension);
extensions::ExtensionPrefs::Get(profile)->OnExtensionInstalled(
extension.get(), {}, {}, extensions::Extension::FROM_WEBSTORE, {},
base::Value::Dict());
base::ScopedAllowBlockingForTesting allow_blocking;
const auto indexeddb_path =
profile->GetPath()
.AppendASCII("IndexedDB")
.AppendASCII(base::StrCat(
{"chrome-extension_", kExtensionId, "_indexeddb.test"}));
const auto local_store_path =
profile->GetPath()
.Append(extensions::kLocalExtensionSettingsDirectoryName)
.AppendASCII(kExtensionId);
// Simulate pref data.
auto store_factory =
base::MakeRefCounted<value_store::TestValueStoreFactory>(
local_store_path);
auto source_store = store_factory->CreateValueStore(
base::FilePath(extensions::kLocalExtensionSettingsDirectoryName),
kExtensionId);
source_store->Set(value_store::ValueStore::DEFAULTS, "id",
base::Value(kExtensionId));
base::CreateDirectory(indexeddb_path);
base::WriteFile(indexeddb_path.AppendASCII("test"), "test");
// Wait for prefs are written on the disk.
while (!GetImportableChromeExtensionsList(profile->GetPath())) {
NonBlockingDelay(base::Milliseconds(10));
}
}
};
IN_PROC_BROWSER_TEST_F(BraveImporterBrowserTest, ImportExtensions) {
auto* source_profile = CreateProfile();
AddTestExtension(source_profile);
for (int i = 0; i < 3; ++i) {
Profile* target = CreateProfile();
// Deletes itself.
auto* host = new BraveExternalProcessImporterHost;
testing::NiceMock<TestObserver> observer;
base::RunLoop run_loop;
EXPECT_CALL(observer, ImportEnded())
.WillOnce(
::testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
host->set_observer(&observer);
importer::SourceProfile source;
source.importer_type = importer::TYPE_CHROME;
source.source_path = source_profile->GetPath();
bool extension_imported = false;
extensions_import::ExtensionsImporter::GetExtensionInstallerForTesting() =
base::BindLambdaForTesting(
[&](const std::string& id)
-> extensions_import::ExtensionImportStatus {
EXPECT_EQ(id, kExtensionId);
extension_imported = true;
return extensions_import::ExtensionImportStatus::kOk;
});
host->StartImportSettings(source, target, importer::EXTENSIONS,
new ProfileWriter(target));
run_loop.Run();
EXPECT_TRUE(extension_imported);
EXPECT_EQ(kExtensionId, ReadStore(target));
}
}
@@ -146,6 +146,7 @@ bool ImportIndexedDBExtensionSettings(const std::string& extension_id,
// Copy settings.
source_enumerator.ForEach([&target_profile,
&kIndexedDBDir](const base::FilePath& path) {
base::CreateDirectory(target_profile.AppendASCII(kIndexedDBDir));
base::CopyDirectory(
path, target_profile.AppendASCII(kIndexedDBDir).Append(path.BaseName()),
true);
+13 -3
View File
@@ -70,12 +70,22 @@ std::vector<std::string> GetImportableListFromChromeExtensionsList(
if (dict.FindBool("was_installed_by_default").value_or(true))
continue;
// `"state": 0` means disabled state
if (!dict.FindInt("state").value_or(false))
const auto state = dict.FindInt("state");
if (state.has_value()) {
// If `state` exists, probably it is an old browser version.
if (state == 0) {
// explicit `"state": 0` means disabled state
continue;
}
} else if (const auto* disable_reasons = dict.FindList("disable_reasons");
disable_reasons && !disable_reasons->empty()) {
// For new browsers an extension is enabled if there is no disable reason.
continue;
}
if (!dict.FindBool("from_webstore").value_or(false))
if (!dict.FindBool("from_webstore").value_or(false)) {
continue;
}
if (auto* manifest_dict = dict.FindDict("manifest")) {
if (Manifest::GetTypeFromManifestValue(*manifest_dict) ==
+1
View File
@@ -1212,6 +1212,7 @@ test("brave_browser_tests") {
]
deps += [
"//brave/browser/extensions/api/identity/test:browsertests",
"//brave/browser/importer:browser_tests",
"//brave/components/brave_webtorrent/browser",
]
}