#include "chrome/browser/spellchecker/spellcheck_service.h"
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
#include "chrome/browser/spellchecker/spell_check_initialization_host_impl.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/spellcheck/browser/pref_names.h"
#include "components/spellcheck/common/spellcheck.mojom.h"
#include "components/spellcheck/common/spellcheck_common.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "components/spellcheck/common/spellcheck_result.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "components/sync/base/command_line_switches.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_utils.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#endif
#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
#include "components/spellcheck/common/spellcheck_features.h"
#endif
BrowserContext;
RenderProcessHost;
class SpellcheckServiceBrowserTest : public InProcessBrowserTest,
public spellcheck::mojom::SpellChecker { … };
class SpellcheckServiceHostBrowserTest : public SpellcheckServiceBrowserTest { … };
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
DisableSpellcheckDisableSpellingService) { … }
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
DisableSpellcheckIfDictionaryIsEmpty) { … }
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
RemoveSpellcheckLanguageFromAcceptLanguages) {
InitSpellcheck(true, "", "en-US,fr");
SetAcceptLanguages("en-US,es,ru");
EXPECT_EQ("en-US,fr", GetMultilingualDictionaries());
}
#else
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
RemoveSpellcheckLanguageFromAcceptLanguages) { … }
#endif
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
KeepSpellcheckLanguagesInAcceptLanguages) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest, StartWithSpellcheck) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
StartWithSingularLanguagePreference) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
StartWithMultiLanguagePreference) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
StartWithBothLanguagePreferences) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
DISABLED_StartWithoutLanguages) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest, StartWithoutSpellcheck) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest, CustomDictionaryChanged) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
CustomDictionaryChangedAfterRendererCrash) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, RequestDictionary) { … }
#if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, CallSpellingService) { … }
#endif
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest, DeleteCorruptedBDICT) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest, PreferencesMigrated) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest, PreferencesNotMigrated) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
SpellcheckingDisabledPreferenceMigration) { … }
IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
MultilingualPreferenceNotMigrated) { … }
#if BUILDFLAG(IS_WIN)
class SpellcheckServiceWindowsHybridBrowserTest
: public SpellcheckServiceBrowserTest {
public:
SpellcheckServiceWindowsHybridBrowserTest()
: SpellcheckServiceBrowserTest(true) {}
};
IN_PROC_BROWSER_TEST_F(SpellcheckServiceWindowsHybridBrowserTest,
WindowsHybridSpellcheck) {
if (base::FeatureList::IsEnabled(spellcheck::kWinDelaySpellcheckServiceInit))
return;
ASSERT_TRUE(spellcheck::UseBrowserSpellChecker());
SpellcheckService* service = static_cast<SpellcheckService*>(
SpellcheckServiceFactory::GetInstance()->GetServiceForBrowserContext(
GetContext(), false));
ASSERT_NE(nullptr, service);
EXPECT_TRUE(service->dictionaries_loaded());
EXPECT_FALSE(service->windows_spellcheck_dictionary_map_.empty());
}
class SpellcheckServiceWindowsHybridBrowserTestDelayInit
: public SpellcheckServiceBrowserTest {
public:
SpellcheckServiceWindowsHybridBrowserTestDelayInit()
: SpellcheckServiceBrowserTest(true) {}
void SetUp() override {
feature_list_.InitAndEnableFeature(
spellcheck::kWinDelaySpellcheckServiceInit);
first_run::ResetCachedSentinelDataForTesting();
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kForceFirstRun);
InProcessBrowserTest::SetUp();
}
void OnDictionariesInitialized() {
dictionaries_initialized_received_ = true;
if (quit_on_callback_)
std::move(quit_on_callback_).Run();
}
protected:
void RunUntilCallbackReceived() {
if (dictionaries_initialized_received_)
return;
base::RunLoop run_loop;
quit_on_callback_ = run_loop.QuitClosure();
run_loop.Run();
dictionaries_initialized_received_ = false;
}
private:
bool dictionaries_initialized_received_ = false;
base::OnceClosure quit_on_callback_;
};
const std::vector<std::string> kWindowsSpellcheckLanguages = {
"fi-FI",
"fr-FR",
"pt-BR"
};
const char kAcceptLanguages[] = "fi-FI,fi,ar-AR,fr-FR,fr,hr,ceb,pt-BR,pt";
const std::vector<std::string> kSpellcheckDictionariesBefore = {
"ar",
"fr-FR",
"fr",
"hr",
"ceb",
"pt-BR",
"pt"
};
const std::vector<std::string> kSpellcheckDictionariesAfter = {
"fi",
"fr-FR",
"fr",
"hr",
"pt-BR",
"pt"
};
IN_PROC_BROWSER_TEST_F(SpellcheckServiceWindowsHybridBrowserTestDelayInit,
PRE_WindowsHybridSpellcheckDelayInit) {
GetPrefs()->SetString(language::prefs::kSelectedLanguages, kAcceptLanguages);
base::Value::List spellcheck_dictionaries_list;
for (const auto& dictionary : kSpellcheckDictionariesBefore) {
spellcheck_dictionaries_list.Append(std::move(dictionary));
}
GetPrefs()->SetList(spellcheck::prefs::kSpellCheckDictionaries,
std::move(spellcheck_dictionaries_list));
}
IN_PROC_BROWSER_TEST_F(SpellcheckServiceWindowsHybridBrowserTestDelayInit,
WindowsHybridSpellcheckDelayInit) {
ASSERT_TRUE(spellcheck::UseBrowserSpellChecker());
SpellcheckService* service = static_cast<SpellcheckService*>(
SpellcheckServiceFactory::GetInstance()->GetServiceForBrowserContext(
GetContext(), false));
EXPECT_EQ(nullptr, service);
service = static_cast<SpellcheckService*>(
SpellcheckServiceFactory::GetInstance()->GetServiceForBrowserContext(
GetContext(), true));
ASSERT_NE(nullptr, service);
EXPECT_FALSE(service->dictionaries_loaded());
EXPECT_TRUE(service->windows_spellcheck_dictionary_map_.empty());
service->AddSpellcheckLanguagesForTesting(kWindowsSpellcheckLanguages);
service->InitializeDictionaries(
base::BindOnce(&SpellcheckServiceWindowsHybridBrowserTestDelayInit::
OnDictionariesInitialized,
base::Unretained(this)));
RunUntilCallbackReceived();
EXPECT_TRUE(service->dictionaries_loaded());
std::map<std::string, std::string>
windows_spellcheck_dictionary_map_first_call =
service->windows_spellcheck_dictionary_map_;
EXPECT_FALSE(windows_spellcheck_dictionary_map_first_call.empty());
EXPECT_EQ(kAcceptLanguages,
GetPrefs()->GetString(language::prefs::kAcceptLanguages));
const base::Value::List& dictionaries_list =
GetPrefs()->GetList(spellcheck::prefs::kSpellCheckDictionaries);
std::vector<std::string> actual_dictionaries;
for (const auto& dictionary : dictionaries_list) {
actual_dictionaries.push_back(dictionary.GetString());
}
EXPECT_EQ(kSpellcheckDictionariesAfter, actual_dictionaries);
service->InitializeDictionaries(
base::BindOnce(&SpellcheckServiceWindowsHybridBrowserTestDelayInit::
OnDictionariesInitialized,
base::Unretained(this)));
RunUntilCallbackReceived();
EXPECT_TRUE(service->dictionaries_loaded());
EXPECT_EQ(windows_spellcheck_dictionary_map_first_call,
service->windows_spellcheck_dictionary_map_);
}
#endif