Files
electron/patches/chromium/revert_cleanup_remove_feature_windelayspellcheckserviceinit.patch
electron-roller[bot] ba135e2f7f chore: bump chromium to 144.0.7506.0 (main) (#48744)
* chore: bump chromium in DEPS to 144.0.7504.0

* chore: bump chromium in DEPS to 144.0.7506.0

* chore: update patches

* Revert "build: explicitly disable reclient"

This reverts commit e08c6adb08.

No longer needed after https://crrev.com/c/7099239

* 7097498: Remove MSG_ROUTING_* constants from ipc_message.h

https://chromium-review.googlesource.com/c/chromium/src/+/7097498

* 7090671: [//gpu] Remove unneeded GpuInfo methods

https://chromium-review.googlesource.com/c/chromium/src/+/7090671

* 7103701: Remove IPC::PlatformFileForTransit.

https://chromium-review.googlesource.com/c/chromium/src/+/7103701

(This should have been removed with https://github.com/electron/electron/pull/17406).

* chore: update filenames.libcxx.gni

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2025-11-03 21:26:16 -08:00

766 lines
34 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Charles Kerr <charles@charleskerr.com>
Date: Tue, 21 Oct 2025 21:45:33 -0500
Subject: Revert "[cleanup] Remove feature WinDelaySpellcheckServiceInit"
This reverts commit 5fe1e59226f59c4d6fb70e7410d1a0ab83688ae2.
Our codebase currently needs the ability to delay this service.
It was added in c2d7164 (#38248) to fix a crash originally
described in 97b353a (#34993):
> Delaying spell check initialization is causing specs for
> 'custom dictionary word list API' to fail in Electron.
This patch can be removed when we fix that crash.
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 2034b961d99225ebe9b606af915f5d90fdae913e..a7ee864ae4d14d36bdf5f7f4fb0ba86255dc9c6f 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -1475,6 +1475,17 @@ void ChromeBrowserMainParts::PostProfileInit(Profile* profile,
profile->GetPath()));
}
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+ // Create the spellcheck service. This will asynchronously retrieve the
+ // Windows platform spellcheck dictionary language tags used to populate the
+ // context menu for editable content.
+ if (spellcheck::UseBrowserSpellChecker() &&
+ profile->GetPrefs()->GetBoolean(spellcheck::prefs::kSpellCheckEnable) &&
+ !base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit)) {
+ SpellcheckServiceFactory::GetForContext(profile);
+ }
+#endif
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || \
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
index 3e617c9bf924e089e27784f960f08a21b98fee0a..8fd91821d588b5d70d1b320b64cc640c77be8d7d 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
@@ -275,15 +275,21 @@ LanguageSettingsPrivateGetLanguageListFunction::Run() {
#if BUILDFLAG(IS_WIN)
if (spellcheck::UseBrowserSpellChecker()) {
- // Asynchronously load the dictionaries to determine platform support.
- SpellcheckService* service =
- SpellcheckServiceFactory::GetForContext(browser_context());
- AddRef(); // Balanced in OnDictionariesInitialized
- service->InitializeDictionaries(
- base::BindOnce(&LanguageSettingsPrivateGetLanguageListFunction::
- OnDictionariesInitialized,
- base::Unretained(this)));
- return RespondLater();
+ if (!base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit)) {
+ // Platform dictionary support already determined at browser startup.
+ UpdateSupportedPlatformDictionaries();
+ } else {
+ // Asynchronously load the dictionaries to determine platform support.
+ SpellcheckService* service =
+ SpellcheckServiceFactory::GetForContext(browser_context());
+ AddRef(); // Balanced in OnDictionariesInitialized
+ service->InitializeDictionaries(
+ base::BindOnce(&LanguageSettingsPrivateGetLanguageListFunction::
+ OnDictionariesInitialized,
+ base::Unretained(this)));
+ return RespondLater();
+ }
}
#endif // BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
index 272d50c9b060a8984a15d2dfbf2297e931482b57..e74b37ed7e596ea57728694eab9a398e94bde08c 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
@@ -116,6 +116,8 @@ class LanguageSettingsPrivateApiTest : public ExtensionServiceTestBase {
protected:
void RunGetLanguageListTest();
+ virtual void InitFeatures() {}
+
#if BUILDFLAG(IS_WIN)
virtual void AddSpellcheckLanguagesForTesting(
const std::vector<std::string>& spellcheck_languages_for_testing) {
@@ -134,6 +136,8 @@ class LanguageSettingsPrivateApiTest : public ExtensionServiceTestBase {
EventRouterFactory::GetInstance()->SetTestingFactory(
profile(), base::BindRepeating(&BuildEventRouter));
+ InitFeatures();
+
LanguageSettingsPrivateDelegateFactory::GetInstance()->SetTestingFactory(
profile(), base::BindRepeating(&BuildLanguageSettingsPrivateDelegate));
@@ -291,6 +295,28 @@ TEST_F(LanguageSettingsPrivateApiTest, GetNeverTranslateLanguagesListTest) {
}
}
+class LanguageSettingsPrivateApiGetLanguageListTest
+ : public LanguageSettingsPrivateApiTest {
+ public:
+ LanguageSettingsPrivateApiGetLanguageListTest() = default;
+ ~LanguageSettingsPrivateApiGetLanguageListTest() override = default;
+
+ protected:
+ void InitFeatures() override {
+#if BUILDFLAG(IS_WIN)
+ // Disable the delayed init feature since that case is tested in
+ // LanguageSettingsPrivateApiTestDelayInit below.
+ feature_list_.InitAndDisableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+#endif // BUILDFLAG(IS_WIN)
+ }
+};
+
+TEST_F(LanguageSettingsPrivateApiGetLanguageListTest, GetLanguageList) {
+ translate::TranslateDownloadManager::GetInstance()->ResetForTesting();
+ RunGetLanguageListTest();
+}
+
void LanguageSettingsPrivateApiTest::RunGetLanguageListTest() {
struct LanguageToTest {
std::string accept_language;
@@ -700,6 +726,13 @@ class LanguageSettingsPrivateApiTestDelayInit
LanguageSettingsPrivateApiTestDelayInit() = default;
protected:
+ void InitFeatures() override {
+ // Force Windows hybrid spellcheck and delayed initialization of the
+ // spellcheck service to be enabled.
+ feature_list_.InitAndEnableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+ }
+
void AddSpellcheckLanguagesForTesting(
const std::vector<std::string>& spellcheck_languages_for_testing)
override {
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
index bd0d77323277ee422ec4f75cee9f7d4e9bf198b3..d0ed451fe104b9671d6002ca72e8ca3a620cb111 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
@@ -30,7 +30,7 @@ namespace {
// accesses resources.
class SpellingMenuObserverTest : public InProcessBrowserTest {
public:
- SpellingMenuObserverTest() = default;
+ SpellingMenuObserverTest();
void SetUpOnMainThread() override {
Reset(false);
@@ -54,12 +54,6 @@ class SpellingMenuObserverTest : public InProcessBrowserTest {
content::BrowserContext* context) {
auto spellcheck_service = std::make_unique<SpellcheckService>(context);
- // With delayed initialization, we need to initialize dictionaries.
- spellcheck_service->InitializeDictionaries(
- base::BindOnce(&SpellingMenuObserverTest::OnSuggestionsComplete,
- base::Unretained(this)));
- RunUntilCallbackReceived();
-
// Call SetLanguage to assure that the platform spellchecker is initialized.
spellcheck_platform::SetLanguage(
spellcheck_service->platform_spell_checker(), "en-US",
@@ -173,6 +167,16 @@ class SpellingMenuObserverTest : public InProcessBrowserTest {
#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
};
+#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+SpellingMenuObserverTest::SpellingMenuObserverTest() {
+ feature_list_.InitWithFeatures(
+ /*enabled_features=*/{},
+ /*disabled_features=*/{spellcheck::kWinDelaySpellcheckServiceInit});
+}
+#else
+SpellingMenuObserverTest::SpellingMenuObserverTest() = default;
+#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
SpellingMenuObserverTest::~SpellingMenuObserverTest() = default;
} // namespace
diff --git a/chrome/browser/site_isolation/spellcheck_per_process_browsertest.cc b/chrome/browser/site_isolation/spellcheck_per_process_browsertest.cc
index 081e422f9a29fc36eea19ed730b430ba5ceb2861..7817bc7a6f9f2c7fdeb08c9b185d2dd3c8cc61f7 100644
--- a/chrome/browser/site_isolation/spellcheck_per_process_browsertest.cc
+++ b/chrome/browser/site_isolation/spellcheck_per_process_browsertest.cc
@@ -134,21 +134,26 @@ class MockSpellCheckHost : spellcheck::mojom::SpellCheckHost {
#if BUILDFLAG(IS_WIN)
void InitializeDictionaries(
InitializeDictionariesCallback callback) override {
- SpellcheckService* spellcheck = SpellcheckServiceFactory::GetForContext(
- process_host()->GetBrowserContext());
-
- if (!spellcheck) { // Teardown.
- std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
- /*enable=*/false);
+ if (base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit)) {
+ SpellcheckService* spellcheck = SpellcheckServiceFactory::GetForContext(
+ process_host()->GetBrowserContext());
+
+ if (!spellcheck) { // Teardown.
+ std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
+ /*enable=*/false);
+ return;
+ }
+
+ dictionaries_loaded_callback_ = std::move(callback);
+
+ spellcheck->InitializeDictionaries(
+ base::BindOnce(&MockSpellCheckHost::OnDictionariesInitialized,
+ base::Unretained(this)));
return;
}
- dictionaries_loaded_callback_ = std::move(callback);
-
- spellcheck->InitializeDictionaries(
- base::BindOnce(&MockSpellCheckHost::OnDictionariesInitialized,
- base::Unretained(this)));
- return;
+ NOTREACHED();
}
void OnDictionariesInitialized() {
@@ -263,7 +268,17 @@ class ChromeSitePerProcessSpellCheckTest : public ChromeSitePerProcessTest {
blink::features::kRestrictSpellingAndGrammarHighlights);
}
- void SetUp() override { ChromeSitePerProcessTest::SetUp(); }
+ void SetUp() override {
+#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+ // When delayed initialization of the spellcheck service is enabled by
+ // default, want to maintain test coverage for the older code path that
+ // initializes spellcheck on browser startup.
+ feature_list_.InitAndDisableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+ ChromeSitePerProcessTest::SetUp();
+ }
protected:
// Tests that spelling in out-of-process subframes is checked.
@@ -370,3 +385,29 @@ IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessSpellCheckTest,
EXPECT_TRUE(host->SpellingPanelVisible());
}
#endif // BUILDFLAG(HAS_SPELLCHECK_PANEL)
+
+#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+class ChromeSitePerProcessSpellCheckTestDelayInit
+ : public ChromeSitePerProcessSpellCheckTest {
+ public:
+ ChromeSitePerProcessSpellCheckTestDelayInit() = default;
+
+ void SetUp() override {
+ // Don't initialize the SpellcheckService on browser launch.
+ feature_list_.InitAndEnableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+
+ ChromeSitePerProcessTest::SetUp();
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessSpellCheckTestDelayInit,
+ OOPIFSpellCheckTest) {
+ RunOOPIFSpellCheckTest();
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessSpellCheckTestDelayInit,
+ OOPIFDisabledSpellCheckTest) {
+ RunOOPIFDisabledSpellCheckTest();
+}
+#endif // BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
diff --git a/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc b/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
index 4eda6aba66af11df4e8719e075e807564d54baac..29d9399c78fc6b4d597ab1f0dc53571131bffd95 100644
--- a/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
+++ b/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
@@ -186,21 +186,27 @@ void SpellCheckHostChromeImpl::InitializeDictionaries(
InitializeDictionariesCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- // Initialize the spellcheck service if needed. Initialization must
- // happen on UI thread.
- SpellcheckService* spellcheck = GetSpellcheckService();
+ if (base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit)) {
+ // Initialize the spellcheck service if needed. Initialization must
+ // happen on UI thread.
+ SpellcheckService* spellcheck = GetSpellcheckService();
+
+ if (!spellcheck) { // Teardown.
+ std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
+ /*enable=*/false);
+ return;
+ }
- if (!spellcheck) { // Teardown.
- std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
- /*enable=*/false);
+ dictionaries_loaded_callback_ = std::move(callback);
+
+ spellcheck->InitializeDictionaries(
+ base::BindOnce(&SpellCheckHostChromeImpl::OnDictionariesInitialized,
+ weak_factory_.GetWeakPtr()));
return;
}
- dictionaries_loaded_callback_ = std::move(callback);
-
- spellcheck->InitializeDictionaries(
- base::BindOnce(&SpellCheckHostChromeImpl::OnDictionariesInitialized,
- weak_factory_.GetWeakPtr()));
+ NOTREACHED();
}
void SpellCheckHostChromeImpl::OnDictionariesInitialized() {
diff --git a/chrome/browser/spellchecker/spell_check_host_chrome_impl_win_browsertest.cc b/chrome/browser/spellchecker/spell_check_host_chrome_impl_win_browsertest.cc
index 31ab441aedd69c08c6f1beefa2129e60bd44a7f3..308c90516a9881b82da5cedec5d80208dbccdced 100644
--- a/chrome/browser/spellchecker/spell_check_host_chrome_impl_win_browsertest.cc
+++ b/chrome/browser/spellchecker/spell_check_host_chrome_impl_win_browsertest.cc
@@ -32,7 +32,12 @@ class SpellCheckHostChromeImplWinBrowserTest : public InProcessBrowserTest {
public:
SpellCheckHostChromeImplWinBrowserTest() = default;
- void SetUp() override { InProcessBrowserTest::SetUp(); }
+ void SetUp() override {
+ // Don't delay initialization of the SpellcheckService on browser launch.
+ feature_list_.InitAndDisableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+ InProcessBrowserTest::SetUp();
+ }
void SetUpOnMainThread() override {
content::BrowserContext* context = browser()->profile();
@@ -50,22 +55,7 @@ class SpellCheckHostChromeImplWinBrowserTest : public InProcessBrowserTest {
void TearDownOnMainThread() override { renderer_.reset(); }
- void InitializeSpellcheckService() {
- spell_check_host_->InitializeDictionaries(base::BindOnce(
- &SpellCheckHostChromeImplWinBrowserTest::InitializeDictionariesCallback,
- base::Unretained(this)));
- RunUntilResultReceived();
- }
-
- void InitializeDictionariesCallback(
- std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
- const std::vector<std::string>& custom_words,
- bool enable) {
- received_result_ = true;
- if (quit_) {
- std::move(quit_).Run();
- }
- }
+ virtual void InitializeSpellcheckService() {}
void OnSpellcheckResult(const std::vector<SpellCheckResult>& result) {
received_result_ = true;
@@ -139,3 +129,41 @@ void SpellCheckHostChromeImplWinBrowserTest::RunSpellCheckReturnMessageTest() {
EXPECT_EQ(result_[0].length, 2);
EXPECT_EQ(result_[0].decoration, SpellCheckResult::SPELLING);
}
+
+class SpellCheckHostChromeImplWinBrowserTestDelayInit
+ : public SpellCheckHostChromeImplWinBrowserTest {
+ public:
+ SpellCheckHostChromeImplWinBrowserTestDelayInit() = default;
+
+ void SetUp() override {
+ // Don't initialize the SpellcheckService on browser launch.
+ feature_list_.InitAndEnableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+ InProcessBrowserTest::SetUp();
+ }
+
+ void InitializeSpellcheckService() override {
+ // With the kWinDelaySpellcheckServiceInit feature flag set, the spellcheck
+ // service is not initialized when instantiated. Call InitializeDictionaries
+ // to load the dictionaries.
+ spell_check_host_->InitializeDictionaries(
+ base::BindOnce(&SpellCheckHostChromeImplWinBrowserTestDelayInit::
+ InitializeDictionariesCallback,
+ base::Unretained(this)));
+ RunUntilResultReceived();
+ }
+
+ void InitializeDictionariesCallback(
+ std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
+ const std::vector<std::string>& custom_words,
+ bool enable) {
+ received_result_ = true;
+ if (quit_)
+ std::move(quit_).Run();
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(SpellCheckHostChromeImplWinBrowserTestDelayInit,
+ SpellCheckReturnMessage) {
+ RunSpellCheckReturnMessageTest();
+}
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index 97b8fc0f38650e816bcae00e074543766f71e0d0..83d9e177e9b5e13587655915b9fbbddf5f9f275d 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -170,7 +170,9 @@ SpellcheckService::SpellcheckService(content::BrowserContext* context)
custom_dictionary_->Load();
#if BUILDFLAG(IS_WIN)
- if (spellcheck::UseBrowserSpellChecker()) {
+ if (spellcheck::UseBrowserSpellChecker() &&
+ base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit)) {
// If initialization of the spellcheck service is on-demand, it is up to the
// instantiator of the spellcheck service to call InitializeDictionaries
// with a callback.
@@ -493,7 +495,9 @@ void SpellcheckService::LoadDictionaries() {
}
#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
- if (spellcheck::UseBrowserSpellChecker()) {
+ if (base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit) &&
+ spellcheck::UseBrowserSpellChecker()) {
// Only want to fire the callback on first call to LoadDictionaries
// originating from InitializeDictionaries, since supported platform
// dictionaries are cached throughout the browser session and not
@@ -521,7 +525,9 @@ bool SpellcheckService::IsSpellcheckEnabled() const {
bool enable_if_uninitialized = false;
#if BUILDFLAG(IS_WIN)
- if (spellcheck::UseBrowserSpellChecker()) {
+ if (spellcheck::UseBrowserSpellChecker() &&
+ base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit)) {
// If initialization of the spellcheck service is on-demand, the
// renderer-side SpellCheck object needs to start out as enabled in order
// for a click on editable content to initialize the spellcheck service.
diff --git a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
index d99667b86795215717dbb25dfefdf0a32f7cd089..5a6dd4dcc4467601a3197f5b273231b2c95d8cdf 100644
--- a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
@@ -712,6 +712,32 @@ class SpellcheckServiceWindowsHybridBrowserTest
: SpellcheckServiceBrowserTest(/* use_browser_spell_checker=*/true) {}
};
+IN_PROC_BROWSER_TEST_F(SpellcheckServiceWindowsHybridBrowserTest,
+ WindowsHybridSpellcheck) {
+ // This test specifically covers the case where spellcheck delayed
+ // initialization is not enabled, so return early if it is. Other tests
+ // cover the case where delayed initialization is enabled.
+ if (base::FeatureList::IsEnabled(spellcheck::kWinDelaySpellcheckServiceInit))
+ return;
+
+ ASSERT_TRUE(spellcheck::UseBrowserSpellChecker());
+
+ // Note that the base class forces dictionary sync to not be performed, which
+ // on its own would have created a SpellcheckService object. So testing here
+ // that we are still instantiating the SpellcheckService as a browser startup
+ // task to support hybrid spellchecking.
+ SpellcheckService* service = static_cast<SpellcheckService*>(
+ SpellcheckServiceFactory::GetInstance()->GetServiceForBrowserContext(
+ GetContext(), /* create */ false));
+ ASSERT_NE(nullptr, service);
+
+ // The list of Windows spellcheck languages should have been populated by at
+ // least one language. This assures that the spellcheck context menu will
+ // include Windows spellcheck languages that lack Hunspell support.
+ EXPECT_TRUE(service->dictionaries_loaded());
+ EXPECT_FALSE(service->windows_spellcheck_dictionary_map_.empty());
+}
+
class SpellcheckServiceWindowsHybridBrowserTestDelayInit
: public SpellcheckServiceBrowserTest {
public:
@@ -719,6 +745,10 @@ class SpellcheckServiceWindowsHybridBrowserTestDelayInit
: SpellcheckServiceBrowserTest(/* use_browser_spell_checker=*/true) {}
void SetUp() override {
+ // Don't initialize the SpellcheckService on browser launch.
+ feature_list_.InitAndEnableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+
// Add command line switch that forces first run state, to test whether
// primary preferred language has its spellcheck dictionary enabled by
// default for non-Hunspell languages.
@@ -808,9 +838,10 @@ IN_PROC_BROWSER_TEST_F(SpellcheckServiceWindowsHybridBrowserTestDelayInit,
WindowsHybridSpellcheckDelayInit) {
ASSERT_TRUE(spellcheck::UseBrowserSpellChecker());
- // The base class forces dictionary sync to be skipped, so the
- // SpellcheckService object should not have been created on browser startup
- // because. Verify this is the case.
+ // Note that the base class forces dictionary sync to not be performed, and
+ // the kWinDelaySpellcheckServiceInit flag is set, which together should
+ // prevent creation of a SpellcheckService object on browser startup. So
+ // testing here that this is indeed the case.
SpellcheckService* service = static_cast<SpellcheckService*>(
SpellcheckServiceFactory::GetInstance()->GetServiceForBrowserContext(
GetContext(), /* create */ false));
diff --git a/chrome/browser/spellchecker/spellcheck_service_unittest.cc b/chrome/browser/spellchecker/spellcheck_service_unittest.cc
index be073ddd362c6a8e9b93961eba2c5a7d4a978b5e..68c479ca1e8fd93ef661258823122cbc6277ad95 100644
--- a/chrome/browser/spellchecker/spellcheck_service_unittest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_unittest.cc
@@ -181,11 +181,15 @@ class SpellcheckServiceHybridUnitTestBase
protected:
void SetUp() override {
+ InitFeatures();
+
// Use SetTestingFactoryAndUse to force creation and initialization.
SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
&profile_, base::BindRepeating(&BuildSpellcheckService));
}
+ virtual void InitFeatures() {}
+
virtual void InitializeSpellcheckService(
const std::vector<std::string>& spellcheck_languages_for_testing) {
// Fake the presence of Windows spellcheck dictionaries.
@@ -215,6 +219,8 @@ class SpellcheckServiceHybridUnitTestBase
static const std::vector<std::string>
windows_spellcheck_languages_for_testing_;
+ base::test::ScopedFeatureList feature_list_;
+
raw_ptr<SpellcheckService> spellcheck_service_;
};
@@ -327,6 +333,18 @@ const std::vector<std::string> SpellcheckServiceHybridUnitTestBase::
// dictionaries.
};
+class GetDictionariesHybridUnitTestNoDelayInit
+ : public SpellcheckServiceHybridUnitTestBase,
+ public testing::WithParamInterface<TestCase> {
+ protected:
+ void InitFeatures() override {
+ // Disable kWinDelaySpellcheckServiceInit, as the case where it's enabled
+ // is tested in SpellcheckServiceWindowsDictionaryMappingUnitTestDelayInit.
+ feature_list_.InitAndDisableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+ }
+};
+
static const TestCase kHybridGetDictionariesParams[] = {
// Galician (gl) has only Windows support, no Hunspell dictionary. Croatian
// (hr) has only Hunspell support, no local Windows dictionary. First
@@ -379,6 +397,16 @@ static const TestCase kHybridGetDictionariesParams[] = {
TestCase("it,it-IT", {"it", "it-IT"}, {"it", "it-IT"}, {"it", "it-IT"}),
};
+INSTANTIATE_TEST_SUITE_P(TestCases,
+ GetDictionariesHybridUnitTestNoDelayInit,
+ testing::ValuesIn(kHybridGetDictionariesParams));
+
+TEST_P(GetDictionariesHybridUnitTestNoDelayInit, GetDictionaries) {
+ RunGetDictionariesTest(GetParam().accept_languages,
+ GetParam().spellcheck_dictionaries,
+ GetParam().expected_dictionaries);
+}
+
struct DictionaryMappingTestCase {
std::string full_tag;
std::string expected_accept_language;
@@ -401,6 +429,18 @@ std::ostream& operator<<(std::ostream& out,
return out;
}
+class SpellcheckServiceWindowsDictionaryMappingUnitTest
+ : public SpellcheckServiceHybridUnitTestBase,
+ public testing::WithParamInterface<DictionaryMappingTestCase> {
+ protected:
+ void InitFeatures() override {
+ // Disable kWinDelaySpellcheckServiceInit, as the case where it's enabled
+ // is tested in SpellcheckServiceWindowsDictionaryMappingUnitTestDelayInit.
+ feature_list_.InitAndDisableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+ }
+};
+
static const DictionaryMappingTestCase kHybridDictionaryMappingsParams[] = {
DictionaryMappingTestCase({"en-CA", "en-CA", "en-CA", "en", "en"}),
DictionaryMappingTestCase({"en-PH", "en", "en", "", ""}),
@@ -423,6 +463,18 @@ static const DictionaryMappingTestCase kHybridDictionaryMappingsParams[] = {
DictionaryMappingTestCase({"pt-BR", "pt-BR", "pt-BR", "pt", "pt"}),
};
+INSTANTIATE_TEST_SUITE_P(TestCases,
+ SpellcheckServiceWindowsDictionaryMappingUnitTest,
+ testing::ValuesIn(kHybridDictionaryMappingsParams));
+
+TEST_P(SpellcheckServiceWindowsDictionaryMappingUnitTest, CheckMappings) {
+ RunDictionaryMappingTest(
+ GetParam().full_tag, GetParam().expected_accept_language,
+ GetParam().expected_tag_passed_to_spellcheck,
+ GetParam().expected_accept_language_generic,
+ GetParam().expected_tag_passed_to_spellcheck_generic);
+}
+
class SpellcheckServiceHybridUnitTestDelayInitBase
: public SpellcheckServiceHybridUnitTestBase {
public:
@@ -435,6 +487,12 @@ class SpellcheckServiceHybridUnitTestDelayInitBase
}
protected:
+ void InitFeatures() override {
+ // Don't initialize the SpellcheckService on browser launch.
+ feature_list_.InitAndEnableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+ }
+
void InitializeSpellcheckService(
const std::vector<std::string>& spellcheck_languages_for_testing)
override {
diff --git a/components/spellcheck/common/spellcheck_features.cc b/components/spellcheck/common/spellcheck_features.cc
index ab07d966779449efcb0bad95ebe05e6018300048..527fa5d72369bb1194684527312eb093946d41c0 100644
--- a/components/spellcheck/common/spellcheck_features.cc
+++ b/components/spellcheck/common/spellcheck_features.cc
@@ -41,6 +41,8 @@ ScopedDisableBrowserSpellCheckerForTesting::
g_browser_spell_checker_enabled = previous_value_;
}
+BASE_FEATURE(kWinDelaySpellcheckServiceInit, base::FEATURE_ENABLED_BY_DEFAULT);
+
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_ANDROID)
diff --git a/components/spellcheck/common/spellcheck_features.h b/components/spellcheck/common/spellcheck_features.h
index 01e193221c74f0e0bd8620627455f92741448075..7929156c59d078b3d4299bb44ea28bc61bbe0086 100644
--- a/components/spellcheck/common/spellcheck_features.h
+++ b/components/spellcheck/common/spellcheck_features.h
@@ -30,6 +30,25 @@ class ScopedDisableBrowserSpellCheckerForTesting {
const bool previous_value_;
};
+// If the kWinDelaySpellcheckServiceInit feature flag is enabled, don't
+// initialize the spellcheck dictionaries when the SpellcheckService is
+// instantiated. With this flag set: (1) Completing the initialization of the
+// spellcheck service is on-demand, invoked by calling
+// SpellcheckService::InitializeDictionaries with a callback to indicate when
+// the operation completes. (2) The call to create the spellcheck service in
+// ChromeBrowserMainParts::PreMainMessageLoopRunImpl will be skipped. Chromium
+// will still by default instantiate the spellcheck service on startup for
+// custom dictionary synchronization, but will not load Windows spellcheck
+// dictionaries. The command line for launching the browser with Windows hybrid
+// spellchecking enabled but no initialization of the spellcheck service is:
+// chrome
+// --enable-features=WinDelaySpellcheckServiceInit
+// and if instantiation of the spellcheck service needs to be completely
+// disabled:
+// chrome
+// --enable-features=WinDelaySpellcheckServiceInit
+// --disable-sync-types="Dictionary"
+BASE_DECLARE_FEATURE(kWinDelaySpellcheckServiceInit);
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_ANDROID)
diff --git a/components/spellcheck/renderer/spellcheck_provider.cc b/components/spellcheck/renderer/spellcheck_provider.cc
index 20e73dd66865f1d7573adc092d8747e3b3252cfd..0bec57f9a7276c3623edd4fcf009d7b35e453df4 100644
--- a/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/components/spellcheck/renderer/spellcheck_provider.cc
@@ -126,7 +126,9 @@ void SpellCheckProvider::RequestTextChecking(
#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
if (spellcheck::UseBrowserSpellChecker()) {
#if BUILDFLAG(IS_WIN)
- if (!dictionaries_loaded_) {
+ if (base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit) &&
+ !dictionaries_loaded_) {
// Initialize the spellcheck service on demand (this spellcheck request
// could be the result of the first click in editable content), then
// complete the text check request when the dictionaries are loaded.
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.cc b/components/spellcheck/renderer/spellcheck_provider_test.cc
index 04dcb599f5dd93d3e381c243e5ba81fbec8a3790..12c32fd631ff93f1d1ff3c7d2a19924e8909c099 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.cc
+++ b/components/spellcheck/renderer/spellcheck_provider_test.cc
@@ -188,8 +188,14 @@ void TestingSpellCheckProvider::FillSuggestionList(const std::u16string&,
#if BUILDFLAG(IS_WIN)
void TestingSpellCheckProvider::InitializeDictionaries(
InitializeDictionariesCallback callback) {
- std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
- /*enable=*/false);
+ if (base::FeatureList::IsEnabled(
+ spellcheck::kWinDelaySpellcheckServiceInit)) {
+ std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
+ /*enable=*/false);
+ return;
+ }
+
+ NOTREACHED();
}
#endif // BUILDFLAG(IS_WIN)
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
diff --git a/components/spellcheck/renderer/spellcheck_provider_unittest.cc b/components/spellcheck/renderer/spellcheck_provider_unittest.cc
index 487cdb1f871868a7fed8ae2ba1adc816c581d0e2..6e1da0564754063e78ad69997be71bbc95e27b39 100644
--- a/components/spellcheck/renderer/spellcheck_provider_unittest.cc
+++ b/components/spellcheck/renderer/spellcheck_provider_unittest.cc
@@ -65,12 +65,34 @@ class HybridSpellCheckTest
HybridSpellCheckTest() : provider_(&embedder_provider_) {}
~HybridSpellCheckTest() override = default;
+ void SetUp() override {
+ // Don't delay initialization of the SpellcheckService on browser launch.
+ feature_list_.InitAndDisableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+ }
+
+ void RunShouldUseBrowserSpellCheckOnlyWhenNeededTest();
+
protected:
+ base::test::ScopedFeatureList feature_list_;
base::test::SingleThreadTaskEnvironment task_environment_;
spellcheck::EmptyLocalInterfaceProvider embedder_provider_;
TestingSpellCheckProvider provider_;
};
+// Test fixture for testing hybrid check cases with delayed initialization of
+// the spellcheck service.
+class HybridSpellCheckTestDelayInit : public HybridSpellCheckTest {
+ public:
+ HybridSpellCheckTestDelayInit() = default;
+
+ void SetUp() override {
+ // Don't initialize the SpellcheckService on browser launch.
+ feature_list_.InitAndEnableFeature(
+ spellcheck::kWinDelaySpellcheckServiceInit);
+ }
+};
+
// Test fixture for testing combining results from both the native spell checker
// and Hunspell.
class CombineSpellCheckResultsTest
@@ -173,6 +195,10 @@ INSTANTIATE_TEST_SUITE_P(
testing::ValuesIn(kSpellCheckProviderHybridTestsParams));
TEST_P(HybridSpellCheckTest, ShouldUseBrowserSpellCheckOnlyWhenNeeded) {
+ RunShouldUseBrowserSpellCheckOnlyWhenNeededTest();
+}
+
+void HybridSpellCheckTest::RunShouldUseBrowserSpellCheckOnlyWhenNeededTest() {
const auto& test_case = GetParam();
FakeTextCheckingResult completion;
@@ -191,6 +217,20 @@ TEST_P(HybridSpellCheckTest, ShouldUseBrowserSpellCheckOnlyWhenNeeded) {
EXPECT_EQ(completion.cancellation_count_, 0U);
}
+// Tests that the SpellCheckProvider calls into the native spell checker only
+// when needed when the code path through
+// SpellCheckProvider::RequestTextChecking is that used when the spellcheck
+// service is initialized on demand.
+INSTANTIATE_TEST_SUITE_P(
+ SpellCheckProviderHybridTests,
+ HybridSpellCheckTestDelayInit,
+ testing::ValuesIn(kSpellCheckProviderHybridTestsParams));
+
+TEST_P(HybridSpellCheckTestDelayInit,
+ ShouldUseBrowserSpellCheckOnlyWhenNeeded) {
+ RunShouldUseBrowserSpellCheckOnlyWhenNeededTest();
+}
+
// Tests that the SpellCheckProvider can correctly combine results from the
// native spell checker and Hunspell.
INSTANTIATE_TEST_SUITE_P(