Commit 56df41c3 authored by Bruce Long's avatar Bruce Long Committed by Commit Bot

Windows Spellcheck: Fix for delayed init on clicking editable content

This change fixes an issue with the delayed initialization of the
spellcheck service (a feature that in Chromium is currently behind the
WinDelaySpellcheckServiceInit feature flag). While the first click in
the editable content indeed initializes the spellcheck service by
loading dictionaries, it does not guarantee a successful spell check
request. Spelling may not be checked until further typing or focusing
in a different editable element. If the text is long enough
(multilined?), then a single click works.

The bug is caused by a race condition in the mojo calls from the
browser to the renderer process. There is no guarantee that the
callback to the renderer process when InitializeDictionaries completes
will be invoked in the renderer process after the call to
SpellCheck::Initialize that results from the browser method
SpellcheckService::InitForRenderer. If the first call wins the race,
no spellchecking will occur because the spellcheck provider thinks
there are no dictionaries available and does not check spelling.

Mojo does not guarantee ordering in separate message pipes, which
the SpellChecker and SpellCheckHost interfaces comprise. One fix that
I considered was to make these associated interfaces that share a
single channel between the browser and renderer processes. But I
believe this risks breaking other platforms. So the fix I have
here instead ensures that when the InitializeDictionaries callback is
received in the renderer, SpellCheck::Initialize is called with the
list of dictionaries provided by the browser process.

Test coverage is provided by a browser test based on the original test
that caught this bug (ChromeSitePerProcessTest.OOPIFSpellCheckTest):
ChromeSitePerProcessSpellCheckTestDelayInit.OOPIFSpellCheckTest. The
HTML source for this browser test contains just a snippet of editable
content, so putting focus in it to invoke spellchecking and initialize
the spellcheck service on demand exposed the issue.

Bug: 1103847
Change-Id: I809e4aef3d51808d222012ddd0c0b46e3d57098a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2335028Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Reviewed-by: default avatarGuillaume Jenkins <gujen@google.com>
Commit-Queue: Bruce Long <brlong@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#796789}
parent b3625d3d
......@@ -10,11 +10,13 @@
#include "base/bind_helpers.h"
#include "base/feature_list.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/site_isolation/chrome_site_per_process_test.h"
#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
......@@ -22,6 +24,7 @@
#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_features.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
......@@ -130,12 +133,48 @@ class MockSpellCheckHost : spellcheck::mojom::SpellCheckHost {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::move(callback).Run(std::vector<std::vector<base::string16>>());
}
#endif // defined(OS_WIN)
void InitializeDictionaries(
InitializeDictionariesCallback callback) override {
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;
}
NOTREACHED();
std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
/*enable=*/false);
}
void OnDictionariesInitialized() {
if (dictionaries_loaded_callback_) {
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries;
dictionaries.push_back(spellcheck::mojom::SpellCheckBDictLanguage::New(
base::File(), "en-US"));
std::move(dictionaries_loaded_callback_)
.Run(std::move(dictionaries), {}, true);
}
}
// Callback passed as argument to InitializeDictionaries, and invoked when
// the dictionaries are loaded for the first time.
InitializeDictionariesCallback dictionaries_loaded_callback_;
#endif // defined(OS_WIN)
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
#if defined(OS_ANDROID)
......@@ -226,9 +265,27 @@ class SpellCheckBrowserTestHelper {
DISALLOW_COPY_AND_ASSIGN(SpellCheckBrowserTestHelper);
};
// Tests that spelling in out-of-process subframes is checked.
// See crbug.com/638361 for details.
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, OOPIFSpellCheckTest) {
class ChromeSitePerProcessSpellCheckTest : public ChromeSitePerProcessTest {
public:
ChromeSitePerProcessSpellCheckTest() = default;
void SetUp() override {
#if defined(OS_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_.InitWithFeatures(
/*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker},
/*disabled_features=*/{spellcheck::kWinDelaySpellcheckServiceInit});
#endif // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
ChromeSitePerProcessTest::SetUp();
}
protected:
// Tests that spelling in out-of-process subframes is checked.
// See crbug.com/638361 for details.
void RunOOPIFSpellCheckTest() {
SpellCheckBrowserTestHelper spell_check_helper;
GURL main_url(embedded_test_server()->GetURL(
......@@ -247,12 +304,12 @@ IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, OOPIFSpellCheckTest) {
spell_check_host->Wait();
EXPECT_EQ(base::ASCIIToUTF16("zz."), spell_check_host->text());
}
}
// Tests that after disabling spellchecking, spelling in new out-of-process
// subframes is not checked. See crbug.com/789273 for details.
// https://crbug.com/944428
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, OOPIFDisabledSpellCheckTest) {
// Tests that after disabling spellchecking, spelling in new out-of-process
// subframes is not checked. See crbug.com/789273 for details.
// https://crbug.com/944428
void RunOOPIFDisabledSpellCheckTest() {
SpellCheckBrowserTestHelper spell_check_helper;
content::BrowserContext* browser_context =
......@@ -285,6 +342,19 @@ IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, OOPIFDisabledSpellCheckTest) {
EXPECT_FALSE(spell_check_host);
prefs->SetBoolean(spellcheck::prefs::kSpellCheckEnable, true);
}
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessSpellCheckTest,
OOPIFSpellCheckTest) {
RunOOPIFSpellCheckTest();
}
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessSpellCheckTest,
OOPIFDisabledSpellCheckTest) {
RunOOPIFDisabledSpellCheckTest();
}
#if BUILDFLAG(HAS_SPELLCHECK_PANEL)
......@@ -296,7 +366,7 @@ IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest, OOPIFDisabledSpellCheckTest) {
#else
#define MAYBE_OOPIFSpellCheckPanelTest OOPIFSpellCheckPanelTest
#endif
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessSpellCheckTest,
MAYBE_OOPIFSpellCheckPanelTest) {
spellcheck::SpellCheckPanelBrowserTestHelper test_helper;
......@@ -323,3 +393,31 @@ IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
EXPECT_TRUE(host->SpellingPanelVisible());
}
#endif // BUILDFLAG(HAS_SPELLCHECK_PANEL)
#if defined(OS_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_.InitWithFeatures(
/*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker,
spellcheck::kWinDelaySpellcheckServiceInit},
/*disabled_features=*/{});
ChromeSitePerProcessTest::SetUp();
}
};
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessSpellCheckTestDelayInit,
OOPIFSpellCheckTest) {
RunOOPIFSpellCheckTest();
}
IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessSpellCheckTestDelayInit,
OOPIFDisabledSpellCheckTest) {
RunOOPIFDisabledSpellCheckTest();
}
#endif // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
......@@ -13,6 +13,7 @@
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "components/spellcheck/browser/spellcheck_host_metrics.h"
#include "components/spellcheck/browser/spellcheck_platform.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
......@@ -208,24 +209,63 @@ void SpellCheckHostChromeImpl::GetPerLanguageSuggestions(
spellcheck_platform::GetPerLanguageSuggestions(
spellcheck->platform_spell_checker(), word, std::move(callback));
}
#endif // defined(OS_WIN)
void SpellCheckHostChromeImpl::InitializeDictionaries(
InitializeDictionariesCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
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();
std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
/*enable=*/false);
return;
}
dictionaries_loaded_callback_ = std::move(callback);
spellcheck->InitializeDictionaries(
base::BindOnce(&SpellCheckHostChromeImpl::OnDictionariesInitialized,
weak_factory_.GetWeakPtr()));
return;
}
spellcheck->InitializeDictionaries(std::move(callback));
NOTREACHED();
std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
/*enable=*/false);
}
void SpellCheckHostChromeImpl::OnDictionariesInitialized() {
DCHECK(dictionaries_loaded_callback_);
SpellcheckService* spellcheck = GetSpellcheckService();
const bool enable = spellcheck->IsSpellcheckEnabled();
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries;
std::vector<std::string> custom_words;
if (enable) {
for (const auto& hunspell_dictionary :
spellcheck->GetHunspellDictionaries()) {
dictionaries.push_back(spellcheck::mojom::SpellCheckBDictLanguage::New(
hunspell_dictionary->GetDictionaryFile().Duplicate(),
hunspell_dictionary->GetLanguage()));
}
SpellcheckCustomDictionary* custom_dictionary =
spellcheck->GetCustomDictionary();
custom_words.assign(custom_dictionary->GetWords().begin(),
custom_dictionary->GetWords().end());
}
std::move(dictionaries_loaded_callback_)
.Run(std::move(dictionaries), custom_words, enable);
}
#endif // defined(OS_WIN)
void SpellCheckHostChromeImpl::OnRequestFinished(SpellingRequest* request) {
auto iterator = requests_.find(request);
requests_.erase(iterator);
......
......@@ -83,9 +83,9 @@ class SpellCheckHostChromeImpl : public SpellCheckHostImpl {
void GetPerLanguageSuggestions(
const base::string16& word,
GetPerLanguageSuggestionsCallback callback) override;
#endif // defined(OS_WIN)
void InitializeDictionaries(InitializeDictionariesCallback callback) override;
#endif // defined(OS_WIN)
// Clears a finished request from |requests_|. Exposed to SpellingRequest.
void OnRequestFinished(SpellingRequest* request);
......@@ -95,6 +95,14 @@ class SpellCheckHostChromeImpl : public SpellCheckHostImpl {
std::vector<SpellCheckResult>* remote_results,
const std::vector<SpellCheckResult>& local_results);
#if defined(OS_WIN)
void OnDictionariesInitialized();
// Callback passed as argument to InitializeDictionaries, and invoked when
// the dictionaries are loaded for the first time.
InitializeDictionariesCallback dictionaries_loaded_callback_;
#endif // defined(OS_WIN)
// All pending requests.
std::set<std::unique_ptr<SpellingRequest>, base::UniquePtrComparator>
requests_;
......
......@@ -188,7 +188,10 @@ class SpellCheckHostChromeImplWinBrowserTestDelayInit
RunUntilResultReceived();
}
void InitializeDictionariesCallback() {
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();
......
......@@ -378,33 +378,17 @@ void SpellcheckService::InitForRenderer(content::RenderProcessHost* host) {
if (SpellcheckServiceFactory::GetForContext(context) != this)
return;
const PrefService* prefs = user_prefs::UserPrefs::Get(context);
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries;
bool enable_if_uninitialized = false;
#if defined(OS_WIN)
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.
if (!dictionaries_loaded())
enable_if_uninitialized = true;
}
#endif // defined(OS_WIN)
const bool enable = IsSpellcheckEnabled();
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries;
std::vector<std::string> custom_words;
if (enable) {
for (const auto& hunspell_dictionary : hunspell_dictionaries_) {
dictionaries.push_back(spellcheck::mojom::SpellCheckBDictLanguage::New(
hunspell_dictionary->GetDictionaryFile().Duplicate(),
hunspell_dictionary->GetLanguage()));
}
bool enable = prefs->GetBoolean(spellcheck::prefs::kSpellCheckEnable) &&
(!dictionaries.empty() || enable_if_uninitialized);
std::vector<std::string> custom_words;
if (enable) {
custom_words.assign(custom_dictionary_->GetWords().begin(),
custom_dictionary_->GetWords().end());
}
......@@ -503,6 +487,26 @@ SpellcheckService::GetHunspellDictionaries() {
return hunspell_dictionaries_;
}
bool SpellcheckService::IsSpellcheckEnabled() const {
const PrefService* prefs = user_prefs::UserPrefs::Get(context_);
bool enable_if_uninitialized = false;
#if defined(OS_WIN)
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.
if (!dictionaries_loaded())
enable_if_uninitialized = true;
}
#endif // defined(OS_WIN)
return prefs->GetBoolean(spellcheck::prefs::kSpellCheckEnable) &&
(!hunspell_dictionaries_.empty() || enable_if_uninitialized);
}
bool SpellcheckService::LoadExternalDictionary(std::string language,
std::string locale,
std::string path,
......
......@@ -137,6 +137,10 @@ class SpellcheckService : public KeyedService,
const std::vector<std::unique_ptr<SpellcheckHunspellDictionary>>&
GetHunspellDictionaries();
// Returns whether spellchecking is enabled in preferences and if there are
// dictionaries available.
bool IsSpellcheckEnabled() const;
// Load a dictionary from a given path. Format specifies how the dictionary
// is stored. Return value is true if successful.
bool LoadExternalDictionary(std::string language,
......
......@@ -77,15 +77,15 @@ void SpellCheckHostImpl::GetPerLanguageSuggestions(
// This API requires Chrome-only features.
std::move(callback).Run(std::vector<std::vector<base::string16>>());
}
#endif // defined(OS_WIN)
void SpellCheckHostImpl::InitializeDictionaries(
InitializeDictionariesCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
NOTREACHED();
std::move(callback).Run();
std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
/*enable=*/false);
}
#endif // defined(OS_WIN)
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER) &&
// !BUILDFLAG(ENABLE_SPELLING_SERVICE)
......
......@@ -55,10 +55,9 @@ class SpellCheckHostImpl : public spellcheck::mojom::SpellCheckHost {
void GetPerLanguageSuggestions(
const base::string16& word,
GetPerLanguageSuggestionsCallback callback) override;
#endif // defined(OS_WIN)
void InitializeDictionaries(InitializeDictionariesCallback callback) override;
#endif // defined(OS_WIN)
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER) &&
// !BUILDFLAG(ENABLE_SPELLING_SERVICE)
......
......@@ -82,8 +82,11 @@ interface SpellCheckHost {
(array<array<mojo_base.mojom.String16>> suggestions);
// Completes initialization of the spellcheck service by loading dictionaries.
[EnableIf=USE_BROWSER_SPELLCHECKER]
InitializeDictionaries() => ();
[EnableIf=is_win]
InitializeDictionaries() =>
(array<SpellCheckBDictLanguage> dictionaries,
array<string> custom_words,
bool enable);
};
enum Decoration {
......
......@@ -172,6 +172,15 @@ class SpellCheck : public base::SupportsWeakPtr<SpellCheck>,
// Overridden by tests in spellcheck_provider_test.cc (FakeSpellCheck class).
virtual size_t EnabledLanguageCount();
// spellcheck::mojom::SpellChecker:
// Initialize the SpellCheck object with data provided by the browser process.
// Method is public since called directly in
// SpellCheckProvider::OnRespondInitializeDictionaries.
void Initialize(
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
const std::vector<std::string>& custom_words,
bool enable) override;
private:
friend class SpellCheckTest;
friend class FakeSpellCheck;
......@@ -180,10 +189,6 @@ class SpellCheck : public base::SupportsWeakPtr<SpellCheck>,
RequestSpellCheckMultipleTimesWithoutInitialization);
// spellcheck::mojom::SpellChecker:
void Initialize(
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
const std::vector<std::string>& custom_words,
bool enable) override;
void CustomDictionaryChanged(
const std::vector<std::string>& words_added,
const std::vector<std::string>& words_removed) override;
......
......@@ -143,8 +143,15 @@ void SpellCheckProvider::RequestTextChecking(
// 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.
// The delayed spell check service initialization sequence, starting from
// when the user clicks in editable content, is as follows:
// - SpellcheckProvider::RequestTextChecking (Renderer, this method)
// - SpellCheckHostChromeImpl::InitializeDictionaries (Browser)
// - SpellcheckService::InitializeDictionaries (Browser)
// - SpellCheckHostChromeImpl::OnDictionariesInitialized (Browser)
// - SpellcheckProvider::OnRespondInitializeDictionaries (Renderer)
GetSpellCheckHost().InitializeDictionaries(
base::BindOnce(&SpellCheckProvider::RequestTextCheckingFromBrowser,
base::BindOnce(&SpellCheckProvider::OnRespondInitializeDictionaries,
weak_factory_.GetWeakPtr(), text));
return;
}
......@@ -169,7 +176,6 @@ void SpellCheckProvider::RequestTextCheckingFromBrowser(
const base::string16& text) {
DCHECK(spellcheck::UseBrowserSpellChecker());
#if defined(OS_WIN)
dictionaries_loaded_ = true;
// Determine whether a hybrid check is needed.
bool use_hunspell = spellcheck_->EnabledLanguageCount() > 0;
......@@ -209,6 +215,27 @@ void SpellCheckProvider::RequestTextCheckingFromBrowser(
base::BindOnce(&SpellCheckProvider::OnRespondTextCheck,
weak_factory_.GetWeakPtr(), last_identifier_, text));
}
#if defined(OS_WIN)
void SpellCheckProvider::OnRespondInitializeDictionaries(
const base::string16& text,
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
const std::vector<std::string>& custom_words,
bool enable) {
DCHECK(!dictionaries_loaded_);
dictionaries_loaded_ = true;
// Because the SpellChecker and SpellCheckHost mojo interfaces use different
// channels, there is no guarantee that the SpellChecker::Initialize response
// will be received before the SpellCheckHost::InitializeDictionaries
// callback. If the order is reversed, no spellcheck will be performed since
// the renderer side thinks there are no dictionaries available. Ensure that
// the SpellChecker is initialized before performing a spellcheck.
spellcheck_->Initialize(std::move(dictionaries), custom_words, enable);
RequestTextCheckingFromBrowser(text);
}
#endif // defined(OS_WIN)
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
void SpellCheckProvider::FocusedElementChanged(
......
......@@ -137,6 +137,13 @@ class SpellCheckProvider : public content::RenderFrameObserver,
void RequestTextCheckingFromBrowser(const base::string16& text);
#if defined(OS_WIN)
// Callback for when spellcheck service has been initialized on demand.
void OnRespondInitializeDictionaries(
const base::string16& text,
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
const std::vector<std::string>& custom_words,
bool enable);
// Flag indicating that the spellcheck service has been initialized and
// the dictionaries have been loaded initially. Used to avoid an unnecessary
// mojo call to determine this in every text check request.
......
......@@ -191,20 +191,19 @@ void TestingSpellCheckProvider::GetPerLanguageSuggestions(
GetPerLanguageSuggestionsCallback callback) {
NOTREACHED();
}
#endif // defined(OS_WIN)
void TestingSpellCheckProvider::InitializeDictionaries(
InitializeDictionariesCallback callback) {
#if defined(OS_WIN)
if (base::FeatureList::IsEnabled(
spellcheck::kWinDelaySpellcheckServiceInit)) {
std::move(callback).Run();
std::move(callback).Run(/*dictionaries=*/{}, /*custom_words=*/{},
/*enable=*/false);
return;
}
#endif // defined(OS_WIN)
NOTREACHED();
}
#endif // defined(OS_WIN)
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
#if defined(OS_ANDROID)
......
......@@ -149,11 +149,9 @@ class TestingSpellCheckProvider : public SpellCheckProvider,
void GetPerLanguageSuggestions(
const base::string16& word,
GetPerLanguageSuggestionsCallback callback) override;
#endif // defined(OS_WIN)
void InitializeDictionaries(InitializeDictionariesCallback callback) override;
#endif
#endif // defined(OS_WIN)
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
#if defined(OS_ANDROID)
void DisconnectSessionBridge() override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment