summaryrefslogtreecommitdiff
path: root/chromium/components/spellcheck
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-03-11 11:32:04 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-03-18 13:40:17 +0000
commit31ccca0778db85c159634478b4ec7997f6704860 (patch)
tree3d33fc3afd9d5ec95541e1bbe074a9cf8da12a0e /chromium/components/spellcheck
parent248b70b82a40964d5594eb04feca0fa36716185d (diff)
downloadqtwebengine-chromium-31ccca0778db85c159634478b4ec7997f6704860.tar.gz
BASELINE: Update Chromium to 80.0.3987.136
Change-Id: I98e1649aafae85ba3a83e67af00bb27ef301db7b Reviewed-by: Jüri Valdmann <juri.valdmann@qt.io>
Diffstat (limited to 'chromium/components/spellcheck')
-rw-r--r--chromium/components/spellcheck/BUILD.gn6
-rw-r--r--chromium/components/spellcheck/browser/BUILD.gn11
-rw-r--r--chromium/components/spellcheck/browser/spell_check_host_impl.cc52
-rw-r--r--chromium/components/spellcheck/browser/spell_check_host_impl.h21
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_platform.h51
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_platform_android.cc5
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_platform_mac.mm5
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_platform_win.cc345
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_platform_win_unittest.cc44
-rw-r--r--chromium/components/spellcheck/browser/spelling_service_client.cc113
-rw-r--r--chromium/components/spellcheck/common/BUILD.gn8
-rw-r--r--chromium/components/spellcheck/common/spellcheck.mojom17
-rw-r--r--chromium/components/spellcheck/common/spellcheck_common.cc42
-rw-r--r--chromium/components/spellcheck/common/spellcheck_common.h15
-rw-r--r--chromium/components/spellcheck/common/spellcheck_features.cc31
-rw-r--r--chromium/components/spellcheck/common/spellcheck_features.h18
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck.cc207
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck.h64
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc2
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_panel.cc7
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_panel.h3
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider.cc86
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider.h14
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider_test.cc19
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_provider_test.h12
-rw-r--r--chromium/components/spellcheck/renderer/spellcheck_unittest.cc27
-rw-r--r--chromium/components/spellcheck/renderer/spelling_engine.cc18
-rw-r--r--chromium/components/spellcheck/spellcheck.md35
-rw-r--r--chromium/components/spellcheck/spellcheck_build_features.gni21
29 files changed, 971 insertions, 328 deletions
diff --git a/chromium/components/spellcheck/BUILD.gn b/chromium/components/spellcheck/BUILD.gn
index 0baee9cd74c..486778a9546 100644
--- a/chromium/components/spellcheck/BUILD.gn
+++ b/chromium/components/spellcheck/BUILD.gn
@@ -6,14 +6,14 @@ import("//build/buildflag_header.gni")
import("//components/spellcheck/spellcheck_build_features.gni")
buildflag_header("buildflags") {
- # Name this "build" features to avoid confusion with
- # components/spellcheck/common/spellcheck_features.h which are runtime
- # features.
header = "spellcheck_buildflags.h"
flags = [
"ENABLE_SPELLCHECK=$enable_spellcheck",
"USE_BROWSER_SPELLCHECKER=$use_browser_spellchecker",
"USE_RENDERER_SPELLCHECKER=$use_renderer_spellchecker",
+ "USE_WIN_HYBRID_SPELLCHECKER=$use_win_hybrid_spellchecker",
+ "ENABLE_SPELLING_SERVICE=$enable_spelling_service",
"HAS_SPELLCHECK_PANEL=$has_spellcheck_panel",
+ "USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK=$use_windows_preferred_languages_for_spellcheck",
]
}
diff --git a/chromium/components/spellcheck/browser/BUILD.gn b/chromium/components/spellcheck/browser/BUILD.gn
index 5df0c360ac1..bdb515f78ef 100644
--- a/chromium/components/spellcheck/browser/BUILD.gn
+++ b/chromium/components/spellcheck/browser/BUILD.gn
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//components/spellcheck/spellcheck_build_features.gni")
+
if (is_android) {
import("//build/config/android/rules.gni")
}
@@ -20,10 +22,15 @@ source_set("browser") {
"spellcheck_platform_mac.mm",
"spellchecker_session_bridge_android.cc",
"spellchecker_session_bridge_android.h",
- "spelling_service_client.cc",
- "spelling_service_client.h",
]
+ if (enable_spelling_service) {
+ sources += [
+ "spelling_service_client.cc",
+ "spelling_service_client.h",
+ ]
+ }
+
if (is_win) {
sources += [ "spellcheck_platform_win.cc" ]
}
diff --git a/chromium/components/spellcheck/browser/spell_check_host_impl.cc b/chromium/components/spellcheck/browser/spell_check_host_impl.cc
index a5b082b7107..f2b57ae3a35 100644
--- a/chromium/components/spellcheck/browser/spell_check_host_impl.cc
+++ b/chromium/components/spellcheck/browser/spell_check_host_impl.cc
@@ -5,18 +5,10 @@
#include "components/spellcheck/browser/spell_check_host_impl.h"
#include "content/public/browser/browser_thread.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
SpellCheckHostImpl::SpellCheckHostImpl() = default;
SpellCheckHostImpl::~SpellCheckHostImpl() = default;
-// static
-void SpellCheckHostImpl::Create(
- mojo::PendingReceiver<spellcheck::mojom::SpellCheckHost> receiver) {
- mojo::MakeSelfOwnedReceiver(std::make_unique<SpellCheckHostImpl>(),
- std::move(receiver));
-}
-
void SpellCheckHostImpl::RequestDictionary() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -39,36 +31,47 @@ void SpellCheckHostImpl::CallSpellingService(
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (text.empty())
- mojo::ReportBadMessage(__FUNCTION__);
+ mojo::ReportBadMessage("Requested spelling service with empty text");
// This API requires Chrome-only features.
std::move(callback).Run(false, std::vector<SpellCheckResult>());
}
#endif // BUILDFLAG(USE_RENDERER_SPELLCHECKER)
-#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER) && !BUILDFLAG(ENABLE_SPELLING_SERVICE)
void SpellCheckHostImpl::RequestTextCheck(const base::string16& text,
int route_id,
RequestTextCheckCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (text.empty())
- mojo::ReportBadMessage(__FUNCTION__);
+ mojo::ReportBadMessage("Requested text check with empty text");
-#if defined(OS_ANDROID)
session_bridge_.RequestTextCheck(text, std::move(callback));
-#else
- // This API requires Chrome-only features on the platform.
+}
+
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+void SpellCheckHostImpl::RequestPartialTextCheck(
+ const base::string16& text,
+ int route_id,
+ const std::vector<SpellCheckResult>& partial_results,
+ bool fill_suggestions,
+ RequestPartialTextCheckCallback callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (text.empty())
+ mojo::ReportBadMessage("Requested partial text check with empty text");
+
+ // This API requires Chrome-only features.
std::move(callback).Run(std::vector<SpellCheckResult>());
-#endif
}
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
void SpellCheckHostImpl::CheckSpelling(const base::string16& word,
int route_id,
CheckSpellingCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
- // This API requires Chrome-only features.
+ NOTREACHED();
std::move(callback).Run(false);
}
@@ -76,11 +79,22 @@ void SpellCheckHostImpl::FillSuggestionList(
const base::string16& word,
FillSuggestionListCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ NOTREACHED();
+ std::move(callback).Run({});
+}
+#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER) &&
+ // !BUILDFLAG(ENABLE_SPELLING_SERVICE)
+
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+void SpellCheckHostImpl::GetPerLanguageSuggestions(
+ const base::string16& word,
+ GetPerLanguageSuggestionsCallback callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// This API requires Chrome-only features.
- std::move(callback).Run(std::vector<base::string16>());
+ std::move(callback).Run(std::vector<std::vector<base::string16>>());
}
-#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
#if defined(OS_ANDROID)
void SpellCheckHostImpl::DisconnectSessionBridge() {
diff --git a/chromium/components/spellcheck/browser/spell_check_host_impl.h b/chromium/components/spellcheck/browser/spell_check_host_impl.h
index 45295e2f06b..2749daa3505 100644
--- a/chromium/components/spellcheck/browser/spell_check_host_impl.h
+++ b/chromium/components/spellcheck/browser/spell_check_host_impl.h
@@ -30,9 +30,6 @@ class SpellCheckHostImpl : public spellcheck::mojom::SpellCheckHost {
SpellCheckHostImpl();
~SpellCheckHostImpl() override;
- static void Create(
- mojo::PendingReceiver<spellcheck::mojom::SpellCheckHost> receiver);
-
protected:
// spellcheck::mojom::SpellCheckHost:
void RequestDictionary() override;
@@ -43,10 +40,20 @@ class SpellCheckHostImpl : public spellcheck::mojom::SpellCheckHost {
CallSpellingServiceCallback callback) override;
#endif // BUILDFLAG(USE_RENDERER_SPELLCHECKER)
-#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER) && !BUILDFLAG(ENABLE_SPELLING_SERVICE)
void RequestTextCheck(const base::string16& text,
int route_id,
RequestTextCheckCallback callback) override;
+
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ void RequestPartialTextCheck(
+ const base::string16& text,
+ int route_id,
+ const std::vector<SpellCheckResult>& partial_results,
+ bool fill_suggestions,
+ RequestPartialTextCheckCallback callback) override;
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
void CheckSpelling(const base::string16& word,
int route_id,
CheckSpellingCallback callback) override;
@@ -54,6 +61,12 @@ class SpellCheckHostImpl : public spellcheck::mojom::SpellCheckHost {
FillSuggestionListCallback callback) override;
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ void GetPerLanguageSuggestions(
+ const base::string16& word,
+ GetPerLanguageSuggestionsCallback callback) override;
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
#if defined(OS_ANDROID)
// spellcheck::mojom::SpellCheckHost:
void DisconnectSessionBridge() override;
diff --git a/chromium/components/spellcheck/browser/spellcheck_platform.h b/chromium/components/spellcheck/browser/spellcheck_platform.h
index 8ad82607191..54c22a7d7e3 100644
--- a/chromium/components/spellcheck/browser/spellcheck_platform.h
+++ b/chromium/components/spellcheck/browser/spellcheck_platform.h
@@ -14,24 +14,45 @@
#include "base/callback_forward.h"
#include "base/strings/string16.h"
#include "build/build_config.h"
+#include "components/spellcheck/spellcheck_buildflags.h"
#if defined(OS_WIN)
#include "components/spellcheck/browser/spellcheck_host_metrics.h"
#endif // defined(OS_WIN)
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+#include "components/spellcheck/common/spellcheck_common.h"
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
struct SpellCheckResult;
namespace spellcheck_platform {
-typedef base::OnceCallback<void(
- const std::vector<SpellCheckResult>& /* results */)>
+typedef base::OnceCallback<void(const std::vector<SpellCheckResult>&)>
TextCheckCompleteCallback;
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+typedef base::OnceCallback<void(const spellcheck::PerLanguageSuggestions&)>
+ GetSuggestionsCallback;
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+typedef base::OnceCallback<void(const std::vector<std::string>& /* results */)>
+ RetrieveSupportedLanguagesCompleteCallback;
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK
+
// Get the languages supported by the platform spellchecker and store them in
// |spellcheck_languages|. Note that they must be converted to
// Chromium style codes (en-US not en_US). See spellchecker.cc for a full list.
void GetAvailableLanguages(std::vector<std::string>* spellcheck_languages);
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+// Retrieve language tags for installed Windows language packs that also have
+// spellchecking support.
+void RetrieveSupportedWindowsPreferredLanguages(
+ RetrieveSupportedLanguagesCompleteCallback callback);
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK
+
// Returns the language used for spellchecking on the platform.
std::string GetSpellCheckerLanguage();
@@ -51,11 +72,11 @@ void ShowSpellingPanel(bool show);
// spelling panel need not be displayed for this to work.
void UpdateSpellingPanelWithMisspelledWord(const base::string16& word);
-// Translates the codes used by chrome to the language codes used by os x
-// and checks the given language agains the languages that the current system
-// supports. If the platform-specific spellchecker supports the language,
-// then returns true, otherwise false.
-bool PlatformSupportsLanguage(const std::string& current_language);
+// Asynchronously checks whether the current system's spellchecker supports the
+// given language. If the platform-specific spellchecker supports the language,
+// then the callback is invoked with true, otherwise it is invoked with false.
+void PlatformSupportsLanguage(const std::string& current_language,
+ base::OnceCallback<void(bool)> callback);
// Sets the language for the platform-specific spellchecker asynchronously. The
// callback will be invoked with boolean parameter indicating the status of the
@@ -97,11 +118,25 @@ void IgnoreWord(const base::string16& word);
// document can now be forgotten.
void CloseDocumentWithTag(int tag);
-// Requests an asyncronous spell and grammar checking.
+// Requests an asynchronous spell and grammar checking.
void RequestTextCheck(int document_tag,
const base::string16& text,
TextCheckCompleteCallback callback);
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+// Requests an asynchronous spell and grammar checking for the languages that
+// couldn't be handled by the renderer spellchecker.
+void RequestTextCheck(int document_tag,
+ const base::string16& text,
+ const std::vector<SpellCheckResult>& partial_results,
+ bool fill_suggestions,
+ TextCheckCompleteCallback callback);
+
+// Finds the replacement suggestions for each language for the given word.
+void GetPerLanguageSuggestions(const base::string16& word,
+ GetSuggestionsCallback callback);
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
#if defined(OS_WIN)
// Records how many user spellcheck languages are currently not supported by the
// Windows OS spellchecker due to missing language packs.
diff --git a/chromium/components/spellcheck/browser/spellcheck_platform_android.cc b/chromium/components/spellcheck/browser/spellcheck_platform_android.cc
index 50263d19d2c..b76573cf383 100644
--- a/chromium/components/spellcheck/browser/spellcheck_platform_android.cc
+++ b/chromium/components/spellcheck/browser/spellcheck_platform_android.cc
@@ -35,8 +35,9 @@ void ShowSpellingPanel(bool show) {
void UpdateSpellingPanelWithMisspelledWord(const base::string16& word) {
}
-bool PlatformSupportsLanguage(const std::string& current_language) {
- return true;
+void PlatformSupportsLanguage(const std::string& current_language,
+ base::OnceCallback<void(bool)> callback) {
+ std::move(callback).Run(true);
}
void SetLanguage(const std::string& lang_to_set,
diff --git a/chromium/components/spellcheck/browser/spellcheck_platform_mac.mm b/chromium/components/spellcheck/browser/spellcheck_platform_mac.mm
index b24b1724062..fe35d119138 100644
--- a/chromium/components/spellcheck/browser/spellcheck_platform_mac.mm
+++ b/chromium/components/spellcheck/browser/spellcheck_platform_mac.mm
@@ -151,7 +151,8 @@ void UpdateSpellingPanelWithMisspelledWord(const base::string16& word) {
waitUntilDone:YES];
}
-bool PlatformSupportsLanguage(const std::string& current_language) {
+void PlatformSupportsLanguage(const std::string& current_language,
+ base::OnceCallback<void(bool)> callback) {
// First, convert the language to an OS X language code.
NSString* mac_lang_code = ConvertLanguageCodeToMac(current_language);
@@ -159,7 +160,7 @@ bool PlatformSupportsLanguage(const std::string& current_language) {
NSArray* availableLanguages = [SharedSpellChecker() availableLanguages];
// Return true if the given language is supported by OS X.
- return [availableLanguages containsObject:mac_lang_code];
+ std::move(callback).Run([availableLanguages containsObject:mac_lang_code]);
}
void SetLanguage(const std::string& lang_to_set,
diff --git a/chromium/components/spellcheck/browser/spellcheck_platform_win.cc b/chromium/components/spellcheck/browser/spellcheck_platform_win.cc
index 1ac94e7fa39..c0ecc3081cc 100644
--- a/chromium/components/spellcheck/browser/spellcheck_platform_win.cc
+++ b/chromium/components/spellcheck/browser/spellcheck_platform_win.cc
@@ -6,8 +6,13 @@
#include <objidl.h>
#include <spellcheck.h>
+#include <windows.foundation.collections.h>
+#include <windows.globalization.h>
+#include <windows.system.userprofile.h>
+#include <winnls.h> // ResolveLocaleName
#include <wrl/client.h>
+#include <algorithm>
#include <codecvt>
#include <locale>
#include <string>
@@ -16,10 +21,14 @@
#include "base/callback.h"
#include "base/command_line.h"
#include "base/no_destructor.h"
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/win/com_init_util.h"
+#include "base/win/core_winrt_util.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/scoped_hstring.h"
#include "base/win/windows_types.h"
#include "base/win/windows_version.h"
#include "components/spellcheck/common/spellcheck_common.h"
@@ -28,6 +37,9 @@
namespace spellcheck_platform {
+typedef base::OnceCallback<void(const spellcheck::PerLanguageResult&)>
+ PlatformTextCheckCompleteCallback;
+
namespace {
// WindowsSpellChecker class is used to store all the COM objects and
// control their lifetime. The class also provides wrappers for
@@ -44,9 +56,14 @@ class WindowsSpellChecker {
void DisableSpellChecker(const std::string& lang_tag);
- void RequestTextCheckForAllLanguages(int document_tag,
- const base::string16& text,
- TextCheckCompleteCallback callback);
+ void RequestTextCheckForAllLanguages(
+ int document_tag,
+ const base::string16& text,
+ bool fill_suggestions,
+ PlatformTextCheckCompleteCallback callback);
+
+ void GetPerLanguageSuggestions(const base::string16& word,
+ GetSuggestionsCallback callback);
void AddWordForAllLanguages(const base::string16& word);
@@ -58,6 +75,16 @@ class WindowsSpellChecker {
const std::vector<std::string> spellcheck_locales,
SpellCheckHostMetrics* metrics);
+ void IsLanguageSupported(const std::string& lang_tag,
+ base::OnceCallback<void(bool)> callback);
+
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+ // Retrieve language tags for installed Windows language packs that also have
+ // spellchecking support.
+ void RetrieveSupportedWindowsPreferredLanguages(
+ RetrieveSupportedLanguagesCompleteCallback callback);
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+
private:
void CreateSpellCheckerFactoryInBackgroundThread();
@@ -77,7 +104,12 @@ class WindowsSpellChecker {
void RequestTextCheckForAllLanguagesInBackgroundThread(
int document_tag,
const base::string16& text,
- TextCheckCompleteCallback callback);
+ bool fill_suggestions,
+ PlatformTextCheckCompleteCallback callback);
+
+ void GetPerLanguageSuggestionsInBackgroundThread(
+ const base::string16& word,
+ GetSuggestionsCallback callback);
// Fills the given vector |optional_suggestions| with a number (up to
// kMaxSuggestions) of suggestions for the string |wrong_word| of language
@@ -93,10 +125,15 @@ class WindowsSpellChecker {
void IgnoreWordForAllLanguagesInBackgroundThread(const base::string16& word);
- // Returns true if spellchecker is available for the given language
- // |current_language|. This function must run on the background thread.
- bool IsLanguageSupportedInBackgroundThread(
- const std::string& current_language);
+ // Returns true if a spellchecker is available for the given language
+ // |lang_tag|. This function must run on the background thread.
+ bool IsLanguageSupportedInBackgroundThread(const std::string& lang_tag);
+
+ // Checks if a spellchecker is available for the given language |lang_tag| and
+ // posts the result to a callback on the main thread.
+ void IsLanguageSupportedWithCallbackInBackgroundThread(
+ const std::string& lang_tag,
+ base::OnceCallback<void(bool)> callback);
// Returns true if ISpellCheckerFactory has been initialized.
bool IsSpellCheckerFactoryInitialized();
@@ -116,6 +153,12 @@ class WindowsSpellChecker {
const std::vector<std::string> spellcheck_locales,
SpellCheckHostMetrics* metrics);
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+ // Async retrieval of supported preferred languages.
+ void RetrieveSupportedWindowsPreferredLanguagesInBackgroundThread(
+ RetrieveSupportedLanguagesCompleteCallback callback);
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+
// Spellchecker objects are owned by WindowsSpellChecker class.
Microsoft::WRL::ComPtr<ISpellCheckerFactory> spell_checker_factory_;
std::map<std::string, Microsoft::WRL::ComPtr<ISpellChecker>>
@@ -166,13 +209,24 @@ void WindowsSpellChecker::DisableSpellChecker(const std::string& lang_tag) {
void WindowsSpellChecker::RequestTextCheckForAllLanguages(
int document_tag,
const base::string16& text,
- TextCheckCompleteCallback callback) {
+ bool fill_suggestions,
+ PlatformTextCheckCompleteCallback callback) {
background_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WindowsSpellChecker::
RequestTextCheckForAllLanguagesInBackgroundThread,
weak_ptr_factory_.GetWeakPtr(), document_tag, text,
- std::move(callback)));
+ fill_suggestions, std::move(callback)));
+}
+
+void WindowsSpellChecker::GetPerLanguageSuggestions(
+ const base::string16& word,
+ GetSuggestionsCallback callback) {
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &WindowsSpellChecker::GetPerLanguageSuggestionsInBackgroundThread,
+ weak_ptr_factory_.GetWeakPtr(), word, std::move(callback)));
}
void WindowsSpellChecker::AddWordForAllLanguages(const base::string16& word) {
@@ -201,6 +255,17 @@ void WindowsSpellChecker::IgnoreWordForAllLanguages(
weak_ptr_factory_.GetWeakPtr(), word));
}
+void WindowsSpellChecker::IsLanguageSupported(
+ const std::string& lang_tag,
+ base::OnceCallback<void(bool)> callback) {
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WindowsSpellChecker::
+ IsLanguageSupportedWithCallbackInBackgroundThread,
+ weak_ptr_factory_.GetWeakPtr(), lang_tag,
+ std::move(callback)));
+}
+
void WindowsSpellChecker::RecordMissingLanguagePacksCount(
const std::vector<std::string> spellcheck_locales,
SpellCheckHostMetrics* metrics) {
@@ -212,6 +277,18 @@ void WindowsSpellChecker::RecordMissingLanguagePacksCount(
std::move(spellcheck_locales), metrics));
}
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+void WindowsSpellChecker::RetrieveSupportedWindowsPreferredLanguages(
+ RetrieveSupportedLanguagesCompleteCallback callback) {
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &WindowsSpellChecker::
+ RetrieveSupportedWindowsPreferredLanguagesInBackgroundThread,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK
+
void WindowsSpellChecker::CreateSpellCheckerFactoryInBackgroundThread() {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
base::win::AssertComApartmentType(base::win::ComApartmentType::STA);
@@ -266,25 +343,21 @@ void WindowsSpellChecker::DisableSpellCheckerInBackgroundThread(
void WindowsSpellChecker::RequestTextCheckForAllLanguagesInBackgroundThread(
int document_tag,
const base::string16& text,
- TextCheckCompleteCallback callback) {
+ bool fill_suggestions,
+ PlatformTextCheckCompleteCallback callback) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
- // Construct a map to store spellchecking results. The key of the map is a
- // tuple which contains the start index and the word length of the misspelled
- // word. The value of the map is a vector which contains suggestion lists for
- // each available language.
- std::map<std::tuple<ULONG, ULONG>, std::vector<std::vector<base::string16>>>
- result_map;
+ spellcheck::PerLanguageResult results;
+ std::wstring word_to_check_wide(base::UTF16ToWide(text));
- std::vector<SpellCheckResult> results;
for (auto it = spell_checker_map_.begin(); it != spell_checker_map_.end();
++it) {
- std::wstring word_to_check_wide(base::UTF16ToWide(text));
Microsoft::WRL::ComPtr<IEnumSpellingError> spelling_errors;
HRESULT hr = it->second->ComprehensiveCheck(word_to_check_wide.c_str(),
&spelling_errors);
if (SUCCEEDED(hr) && spelling_errors) {
+ std::vector<SpellCheckResult> language_results;
do {
Microsoft::WRL::ComPtr<ISpellingError> spelling_error;
ULONG start_index = 0;
@@ -298,40 +371,46 @@ void WindowsSpellChecker::RequestTextCheckForAllLanguagesInBackgroundThread(
(action == CORRECTIVE_ACTION_GET_SUGGESTIONS ||
action == CORRECTIVE_ACTION_REPLACE)) {
std::vector<base::string16> suggestions;
- FillSuggestionListInBackgroundThread(
- it->first, text.substr(start_index, error_length), &suggestions);
- result_map[std::tuple<ULONG, ULONG>(start_index, error_length)]
- .push_back(suggestions);
+
+ if (fill_suggestions) {
+ FillSuggestionListInBackgroundThread(
+ it->first, text.substr(start_index, error_length),
+ &suggestions);
+ }
+
+ language_results.push_back(
+ SpellCheckResult(SpellCheckResult::Decoration::SPELLING,
+ start_index, error_length, suggestions));
}
} while (hr == S_OK);
+
+ results.push_back(language_results);
}
}
- // Generates results vector from map. Remove entries if the word is not
- // misspelled for all available laugages.
- for (auto it = result_map.begin(); it != result_map.end();) {
- if (it->second.size() < spell_checker_map_.size()) {
- it = result_map.erase(it);
- } else {
- // Prepare results vector.
- std::vector<base::string16> suggestions_result;
- for (auto suggestions_list : it->second) {
- for (auto suggestions : suggestions_list) {
- suggestions_result.push_back(suggestions);
- }
- }
+ // Runs the callback on the main thread after spellcheck completed.
+ main_task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(std::move(callback), results));
+}
- results.push_back(SpellCheckResult(
- SpellCheckResult::Decoration::SPELLING, std::get<0>(it->first),
- std::get<1>(it->first), suggestions_result));
+void WindowsSpellChecker::GetPerLanguageSuggestionsInBackgroundThread(
+ const base::string16& word,
+ GetSuggestionsCallback callback) {
+ DCHECK(!main_task_runner_->BelongsToCurrentThread());
+ spellcheck::PerLanguageSuggestions suggestions;
+ std::vector<base::string16> language_suggestions;
- ++it;
- }
+ for (auto it = spell_checker_map_.begin(); it != spell_checker_map_.end();
+ ++it) {
+ language_suggestions.clear();
+ FillSuggestionListInBackgroundThread(it->first, word,
+ &language_suggestions);
+ suggestions.push_back(language_suggestions);
}
// Runs the callback on the main thread after spellcheck completed.
- main_task_runner_->PostTask(FROM_HERE,
- base::BindOnce(std::move(callback), results));
+ main_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), std::move(suggestions)));
}
void WindowsSpellChecker::FillSuggestionListInBackgroundThread(
@@ -348,15 +427,14 @@ void WindowsSpellChecker::FillSuggestionListInBackgroundThread(
// Populate the vector of WideStrings.
while (hr == S_OK) {
- wchar_t* suggestion = nullptr;
+ base::win::ScopedCoMem<wchar_t> suggestion;
hr = suggestions->Next(1, &suggestion, nullptr);
if (hr == S_OK) {
base::string16 utf16_suggestion;
- if (base::WideToUTF16(suggestion, wcslen(suggestion),
+ if (base::WideToUTF16(suggestion.get(), wcslen(suggestion),
&utf16_suggestion)) {
optional_suggestions->push_back(utf16_suggestion);
}
- CoTaskMemFree(suggestion);
}
}
}
@@ -396,7 +474,7 @@ void WindowsSpellChecker::IgnoreWordForAllLanguagesInBackgroundThread(
}
bool WindowsSpellChecker::IsLanguageSupportedInBackgroundThread(
- const std::string& current_language) {
+ const std::string& lang_tag) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
if (!IsSpellCheckerFactoryInitialized()) {
@@ -405,13 +483,88 @@ bool WindowsSpellChecker::IsLanguageSupportedInBackgroundThread(
}
BOOL is_language_supported = (BOOL) false;
- std::wstring bcp47_language_tag = base::UTF8ToWide(current_language);
+ std::wstring bcp47_language_tag = base::UTF8ToWide(lang_tag);
HRESULT hr = spell_checker_factory_->IsSupported(bcp47_language_tag.c_str(),
&is_language_supported);
return SUCCEEDED(hr) && is_language_supported;
}
+void WindowsSpellChecker::IsLanguageSupportedWithCallbackInBackgroundThread(
+ const std::string& lang_tag,
+ base::OnceCallback<void(bool)> callback) {
+ bool result = IsLanguageSupportedInBackgroundThread(lang_tag);
+
+ // Run the callback with result on the main thread.
+ main_task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(std::move(callback), result));
+}
+
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+void WindowsSpellChecker::
+ RetrieveSupportedWindowsPreferredLanguagesInBackgroundThread(
+ RetrieveSupportedLanguagesCompleteCallback callback) {
+ DCHECK(!main_task_runner_->BelongsToCurrentThread());
+ std::vector<std::string> supported_languages;
+
+ if (IsSpellCheckerFactoryInitialized() &&
+ // IGlobalizationPreferencesStatics is only available on Win8 and above.
+ spellcheck::WindowsVersionSupportsSpellchecker() &&
+ // Using WinRT and HSTRING.
+ base::win::ResolveCoreWinRTDelayload() &&
+ base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) {
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::System::UserProfile::IGlobalizationPreferencesStatics>
+ globalization_preferences;
+
+ HRESULT hr = base::win::GetActivationFactory<
+ ABI::Windows::System::UserProfile::IGlobalizationPreferencesStatics,
+ RuntimeClass_Windows_System_UserProfile_GlobalizationPreferences>(
+ &globalization_preferences);
+ // Should always succeed under same conditions for which
+ // WindowsVersionSupportsSpellchecker returns true.
+ DCHECK(SUCCEEDED(hr));
+ // Retrieve a vector of Windows preferred languages (that is, installed
+ // language packs listed under system Language Settings).
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Foundation::Collections::IVectorView<HSTRING>>
+ preferred_languages;
+ hr = globalization_preferences->get_Languages(&preferred_languages);
+ DCHECK(SUCCEEDED(hr));
+ uint32_t count = 0;
+ hr = preferred_languages->get_Size(&count);
+ DCHECK(SUCCEEDED(hr));
+ // Expect at least one language pack to be installed by default.
+ DCHECK_GE(count, 0u);
+ for (uint32_t i = 0; i < count; ++i) {
+ HSTRING language;
+ hr = preferred_languages->GetAt(i, &language);
+ DCHECK(SUCCEEDED(hr));
+ base::win::ScopedHString language_scoped(language);
+ // Language tags obtained using Windows.Globalization API
+ // (zh-Hans-CN e.g.) need to be converted to locale names via
+ // ResolveLocaleName before being passed to spell checker API.
+ wchar_t locale_name[LOCALE_NAME_MAX_LENGTH];
+ // ResolveLocaleName can only fail if buffer size insufficient.
+ ::ResolveLocaleName(
+ base::as_wcstr(base::AsStringPiece16(language_scoped.Get())),
+ locale_name, LOCALE_NAME_MAX_LENGTH);
+ // See if the language has a dictionary available. Some preferred
+ // languages have no spellchecking support (zh-CN e.g.).
+ BOOL is_language_supported = FALSE;
+ hr = spell_checker_factory_->IsSupported(locale_name,
+ &is_language_supported);
+ DCHECK(SUCCEEDED(hr));
+ if (is_language_supported)
+ supported_languages.push_back(base::WideToUTF8(locale_name));
+ }
+ }
+
+ main_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), supported_languages));
+}
+#endif // #if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+
bool WindowsSpellChecker::IsSpellCheckerFactoryInitialized() {
return spell_checker_factory_ != nullptr;
}
@@ -454,14 +607,75 @@ std::unique_ptr<WindowsSpellChecker>& GetWindowsSpellChecker() {
{base::ThreadPool(), base::MayBlock()})));
return *win_spell_checker;
}
+
+void PlatformCheckComplete(
+ std::vector<SpellCheckResult> renderer_results,
+ bool fill_suggestions,
+ TextCheckCompleteCallback callback,
+ const spellcheck::PerLanguageResult& platform_results) {
+ // Merge renderer results and platform_results into a single map. The key of
+ // the map is a tuple which contains the start index and the word length of
+ // the misspelled word. The value of the map is a vector which contains
+ // suggestion lists for each available language. This allows to quickly see if
+ // all languages agree about a misspelling, and makes it easier to evenly pick
+ // suggestions from all the different languages.
+ std::vector<SpellCheckResult> final_results;
+ std::map<std::tuple<UINT, UINT>, std::vector<std::vector<base::string16>>>
+ result_map;
+ size_t language_count =
+ std::min(renderer_results.size(), size_t(1)) + platform_results.size();
+
+ for (const auto& language_results : platform_results) {
+ for (const auto& result : language_results) {
+ result_map[std::tuple<UINT, UINT>(result.location, result.length)]
+ .push_back(result.replacements);
+ }
+ }
+
+ for (const auto& result : renderer_results) {
+ result_map[std::tuple<UINT, UINT>(result.location, result.length)]
+ .push_back(result.replacements);
+ }
+
+ for (auto it = result_map.begin(); it != result_map.end();) {
+ if (it->second.size() < language_count) {
+ // Some languages considered this correctly spelled, so ignore this
+ // result.
+ it = result_map.erase(it);
+ } else {
+ if (fill_suggestions) {
+ std::vector<std::vector<base::string16>> per_language_suggestions;
+ for (auto& suggestions_list : it->second) {
+ per_language_suggestions.push_back(suggestions_list);
+ }
+
+ std::vector<base::string16> evenly_filled_suggestions;
+ spellcheck::FillSuggestions(per_language_suggestions,
+ &evenly_filled_suggestions);
+ final_results.push_back(SpellCheckResult(
+ SpellCheckResult::Decoration::SPELLING, std::get<0>(it->first),
+ std::get<1>(it->first), evenly_filled_suggestions));
+ } else {
+ final_results.push_back(
+ SpellCheckResult(SpellCheckResult::Decoration::SPELLING,
+ std::get<0>(it->first), std::get<1>(it->first)));
+ }
+ ++it;
+ }
+ }
+
+ std::move(callback).Run(final_results);
+}
+
} // anonymous namespace
bool SpellCheckerAvailable() {
return true;
}
-bool PlatformSupportsLanguage(const std::string& current_language) {
- return true;
+void PlatformSupportsLanguage(const std::string& lang_tag,
+ base::OnceCallback<void(bool)> callback) {
+ GetWindowsSpellChecker()->IsLanguageSupported(lang_tag, std::move(callback));
}
void SetLanguage(const std::string& lang_to_set,
@@ -487,8 +701,29 @@ void RequestTextCheck(int document_tag,
const base::string16& text,
TextCheckCompleteCallback callback) {
GetWindowsSpellChecker()->RequestTextCheckForAllLanguages(
- document_tag, text, std::move(callback));
+ document_tag, text, true,
+ base::BindOnce(&PlatformCheckComplete, std::vector<SpellCheckResult>(),
+ true, std::move(callback)));
+}
+
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+void RequestTextCheck(int document_tag,
+ const base::string16& text,
+ const std::vector<SpellCheckResult>& renderer_results,
+ bool fill_suggestions,
+ TextCheckCompleteCallback callback) {
+ GetWindowsSpellChecker()->RequestTextCheckForAllLanguages(
+ document_tag, text, fill_suggestions,
+ base::BindOnce(&PlatformCheckComplete, std::move(renderer_results),
+ fill_suggestions, std::move(callback)));
+}
+
+void GetPerLanguageSuggestions(const base::string16& word,
+ GetSuggestionsCallback callback) {
+ GetWindowsSpellChecker()->GetPerLanguageSuggestions(word,
+ std::move(callback));
}
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
void AddWord(const base::string16& word) {
GetWindowsSpellChecker()->AddWordForAllLanguages(word);
@@ -506,6 +741,14 @@ void GetAvailableLanguages(std::vector<std::string>* spellcheck_languages) {
// Not used in Windows
}
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+void RetrieveSupportedWindowsPreferredLanguages(
+ RetrieveSupportedLanguagesCompleteCallback callback) {
+ GetWindowsSpellChecker()->RetrieveSupportedWindowsPreferredLanguages(
+ std::move(callback));
+}
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK
+
int GetDocumentTag() {
return 1; // Not used in Windows
}
diff --git a/chromium/components/spellcheck/browser/spellcheck_platform_win_unittest.cc b/chromium/components/spellcheck/browser/spellcheck_platform_win_unittest.cc
index 576f963c011..23b431bf7f6 100644
--- a/chromium/components/spellcheck/browser/spellcheck_platform_win_unittest.cc
+++ b/chromium/components/spellcheck/browser/spellcheck_platform_win_unittest.cc
@@ -50,18 +50,44 @@ class SpellcheckPlatformWinTest : public testing::Test {
std::move(quit_).Run();
}
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+ void RetrieveSupportedWindowsPreferredLanguagesCallback(
+ const std::vector<std::string>& preferred_languages) {
+ callback_finished_ = true;
+ preferred_languages_ = preferred_languages;
+ for (const auto& preferred_language : preferred_languages_) {
+ DLOG(INFO) << "RetrieveSupportedWindowsPreferredLanguagesCallback: "
+ "Dictionary supported for locale: "
+ << preferred_language;
+ }
+ if (quit_)
+ std::move(quit_).Run();
+ }
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK
+
protected:
bool callback_finished_ = false;
bool set_language_result_;
std::vector<SpellCheckResult> spell_check_results_;
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+ std::vector<std::string> preferred_languages_;
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK
base::OnceClosure quit_;
+ // The WindowsSpellChecker class is instantiated with static storage using
+ // base::NoDestructor (see GetWindowsSpellChecker) and creates its own task
+ // runner. The thread pool is destroyed together with TaskEnvironment when the
+ // test fixture object is destroyed. Therefore without some elaborate
+ // test-only code added to the WindowsSpellChecker class or a means to keep
+ // the TaskEnvironment alive (which would set off leak detection), easiest
+ // approach for now is to add all test coverage for Windows spellchecking to
+ // a single test.
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::UI};
};
-TEST_F(SpellcheckPlatformWinTest, SpellCheckSuggestions_EN_US) {
+TEST_F(SpellcheckPlatformWinTest, SpellCheckAsyncMethods) {
static const struct {
const char* input; // A string to be tested.
const char* suggested_word; // A suggested word that should occur.
@@ -89,6 +115,8 @@ TEST_F(SpellcheckPlatformWinTest, SpellCheckSuggestions_EN_US) {
RunUntilResultReceived();
+ ASSERT_TRUE(set_language_result_);
+
for (const auto& test_case : kTestCases) {
const base::string16 word(base::ASCIIToUTF16(test_case.input));
@@ -113,6 +141,20 @@ TEST_F(SpellcheckPlatformWinTest, SpellCheckSuggestions_EN_US) {
ASSERT_NE(suggestions.end(), position);
}
+
+#if BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK)
+ spellcheck_platform::RetrieveSupportedWindowsPreferredLanguages(
+ base::BindOnce(&SpellcheckPlatformWinTest::
+ RetrieveSupportedWindowsPreferredLanguagesCallback,
+ base::Unretained(this)));
+
+ RunUntilResultReceived();
+
+ ASSERT_LE(1u, preferred_languages_.size());
+ ASSERT_NE(preferred_languages_.end(),
+ std::find(preferred_languages_.begin(), preferred_languages_.end(),
+ "en-US"));
+#endif // BUILDFLAG(USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK
}
} // namespace
diff --git a/chromium/components/spellcheck/browser/spelling_service_client.cc b/chromium/components/spellcheck/browser/spelling_service_client.cc
index 4fc7f4a9902..7b888886b35 100644
--- a/chromium/components/spellcheck/browser/spelling_service_client.cc
+++ b/chromium/components/spellcheck/browser/spelling_service_client.cc
@@ -10,7 +10,6 @@
#include <memory>
#include "base/bind.h"
-#include "base/feature_list.h"
#include "base/json/json_reader.h"
#include "base/json/string_escape.h"
#include "base/memory/ptr_util.h"
@@ -24,7 +23,6 @@
#include "components/prefs/pref_service.h"
#include "components/spellcheck/browser/pref_names.h"
#include "components/spellcheck/common/spellcheck_common.h"
-#include "components/spellcheck/common/spellcheck_features.h"
#include "components/spellcheck/common/spellcheck_result.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
@@ -38,21 +36,12 @@
namespace {
-// The old JSON-RPC endpoint for requesting spell checking and sending user
-// feedback.
-const char kSpellingServiceRpcURL[] = "https://www.googleapis.com/rpc";
-
-// The new REST endpoint for requesting spell checking and sending user
-// feedback.
+// The REST endpoint for requesting spell checking and sending user feedback.
const char kSpellingServiceRestURL[] =
"https://www.googleapis.com/spelling/v%d/spelling/check?key=%s";
// The spellcheck suggestions object key in the JSON response from the spelling
-// service when using the JSON-RPC endpoint.
-const char kMisspellingsRpcPath[] = "result.spellingCheckResponse.misspellings";
-
-// The spellcheck suggestions object key in the JSON response from the spelling
-// service when using the REST endpoint.
+// service.
const char kMisspellingsRestPath[] = "spellingCheckResponse.misspellings";
// The location of error messages in JSON response from spelling service.
@@ -99,37 +88,17 @@ bool SpellingServiceClient::RequestTextCheck(
std::string api_key = google_apis::GetAPIKey();
std::string encoded_text = base::GetQuotedJSONString(text_copy);
- std::string request_body;
-
- if (base::FeatureList::IsEnabled(spellcheck::kSpellingServiceRestApi)) {
- static const char kSpellingRequestRestBodyTemplate[] =
- "{"
- "\"text\":%s,"
- "\"language\":\"%s\","
- "\"originCountry\":\"%s\""
- "}";
-
- request_body = base::StringPrintf(
- kSpellingRequestRestBodyTemplate, encoded_text.c_str(),
- language_code.c_str(), country_code.c_str());
- } else {
- static const char kSpellingRequestRpcBodyTemplate[] =
- "{"
- "\"method\":\"spelling.check\","
- "\"apiVersion\":\"v%d\","
- "\"params\":{"
- "\"text\":%s,"
- "\"language\":\"%s\","
- "\"originCountry\":\"%s\","
- "\"key\":%s"
- "}"
- "}";
-
- request_body = base::StringPrintf(
- kSpellingRequestRpcBodyTemplate, type, encoded_text.c_str(),
- language_code.c_str(), country_code.c_str(),
- base::GetQuotedJSONString(api_key).c_str());
- }
+
+ static const char kSpellingRequestRestBodyTemplate[] =
+ "{"
+ "\"text\":%s,"
+ "\"language\":\"%s\","
+ "\"originCountry\":\"%s\""
+ "}";
+
+ std::string request_body =
+ base::StringPrintf(kSpellingRequestRestBodyTemplate, encoded_text.c_str(),
+ language_code.c_str(), country_code.c_str());
// Create traffic annotation tag.
net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -197,8 +166,8 @@ bool SpellingServiceClient::IsAvailable(content::BrowserContext* context,
ServiceType type) {
const PrefService* pref = user_prefs::UserPrefs::Get(context);
DCHECK(pref);
- // If prefs don't allow spellchecking, if the context is off the record, or if
- // multilingual spellchecking is enabled the spelling service should be
+ // If prefs don't allow spell checking, if enhanced spell check is disabled,
+ // or if the context is off the record, the spelling service should be
// unavailable.
if (!pref->GetBoolean(spellcheck::prefs::kSpellCheckEnable) ||
!pref->GetBoolean(spellcheck::prefs::kSpellCheckUseSpellingService) ||
@@ -232,43 +201,36 @@ void SpellingServiceClient::SetURLLoaderFactoryForTesting(
}
GURL SpellingServiceClient::BuildEndpointUrl(int type) {
- if (base::FeatureList::IsEnabled(spellcheck::kSpellingServiceRestApi)) {
return GURL(base::StringPrintf(kSpellingServiceRestURL, type,
google_apis::GetAPIKey().c_str()));
- } else {
- return GURL(kSpellingServiceRpcURL);
- }
}
bool SpellingServiceClient::ParseResponse(
const std::string& data,
std::vector<SpellCheckResult>* results) {
// Data is in the following format:
- // * result: (only in the RPC API; skipped for the REST API) A root object
- // * spellingCheckResponse: A wrapper object containing the response
- // * mispellings: (optional Array<object>) A list of mistakes for the
- // requested text, with the following format:
- // * charStart: (number) The zero-based start of the misspelled region
- // * charLength: (number) The length of the misspelled region
- // * suggestions: (Array<object>) The suggestions for the misspelled
- // text, with the following format:
- // * suggestion: (string) the suggestion for the correct text
- // * canAutoCorrect (optional boolean) Whether we can use the first
- // suggestion for auto-correction
+ // * spellingCheckResponse: A wrapper object containing the response
+ // * mispellings: (optional Array<object>) A list of mistakes for the
+ // requested text, with the following format:
+ // * charStart: (number) The zero-based start of the misspelled region
+ // * charLength: (number) The length of the misspelled region
+ // * suggestions: (Array<object>) The suggestions for the misspelled
+ // text, with the following format:
+ // * suggestion: (string) the suggestion for the correct text
+ // * canAutoCorrect (optional boolean) Whether we can use the first
+ // suggestion for auto-correction
//
// Example response for "duck goes quisk":
// {
- // "result": { // (Only in the RPC API)
- // "spellingCheckResponse": {
- // "misspellings": [{
- // "charStart": 10,
- // "charLength": 5,
- // "suggestions": [{
- // "suggestion": "quack"
- // }],
- // "canAutoCorrect": false
- // }]
- // }
+ // "spellingCheckResponse": {
+ // "misspellings": [{
+ // "charStart": 10,
+ // "charLength": 5,
+ // "suggestions": [{
+ // "suggestion": "quack"
+ // }],
+ // "canAutoCorrect": false
+ // }]
// }
// }
//
@@ -299,11 +261,8 @@ bool SpellingServiceClient::ParseResponse(
// have misspelled words, it returns an empty JSON. (In this case, its HTTP
// status is 200.) We just return true for this case.
base::ListValue* misspellings = nullptr;
- std::string mispellingsPath =
- base::FeatureList::IsEnabled(spellcheck::kSpellingServiceRestApi)
- ? kMisspellingsRestPath
- : kMisspellingsRpcPath;
- if (!value->GetList(mispellingsPath, &misspellings))
+
+ if (!value->GetList(kMisspellingsRestPath, &misspellings))
return true;
for (size_t i = 0; i < misspellings->GetSize(); ++i) {
diff --git a/chromium/components/spellcheck/common/BUILD.gn b/chromium/components/spellcheck/common/BUILD.gn
index 5fbfa95f383..c25049f7c52 100644
--- a/chromium/components/spellcheck/common/BUILD.gn
+++ b/chromium/components/spellcheck/common/BUILD.gn
@@ -60,4 +60,12 @@ mojom("interfaces") {
if (use_renderer_spellchecker) {
enabled_features += [ "USE_RENDERER_SPELLCHECKER" ]
}
+
+ if (use_win_hybrid_spellchecker) {
+ enabled_features += [ "USE_WIN_HYBRID_SPELLCHECKER" ]
+ }
+
+ if (use_windows_preferred_languages_for_spellcheck) {
+ enabled_features += [ "USE_WINDOWS_PREFERRED_LANGUAGES_FOR_SPELLCHECK" ]
+ }
}
diff --git a/chromium/components/spellcheck/common/spellcheck.mojom b/chromium/components/spellcheck/common/spellcheck.mojom
index 828724bd713..01331e47285 100644
--- a/chromium/components/spellcheck/common/spellcheck.mojom
+++ b/chromium/components/spellcheck/common/spellcheck.mojom
@@ -57,6 +57,17 @@ interface SpellCheckHost {
RequestTextCheck(mojo_base.mojom.String16 text, int32 route_id) =>
(array<SpellCheckResult> results);
+ // Exactly like RequestTextCheck, except the text has already been partially
+ // checked (i.e. only in some locales) by the renderer spell checker, and the
+ // native spell checker should check for the rest of the user's locales. The
+ // renderer's partial results are passed as an array of |SpellCheckResult|.
+ // The platform spell checker needs these results to combine them with the
+ // results from the native checks.
+ [EnableIf=USE_WIN_HYBRID_SPELLCHECKER]
+ RequestPartialTextCheck(mojo_base.mojom.String16 text, int32 route_id,
+ array<SpellCheckResult> partial_results, bool fill_suggestions)
+ => (array<SpellCheckResult> results);
+
// Disconnects the Android spell checker session bridge.
[EnableIf=is_android]
DisconnectSessionBridge();
@@ -74,6 +85,12 @@ interface SpellCheckHost {
[EnableIf=USE_BROWSER_SPELLCHECKER, Sync]
FillSuggestionList(mojo_base.mojom.String16 word) =>
(array<mojo_base.mojom.String16> suggestions);
+
+ // Returns a list of suggestions for each spellcheck language for a given word
+ // with a platform-specific spell checker.
+ [EnableIf=USE_WIN_HYBRID_SPELLCHECKER, Sync]
+ GetPerLanguageSuggestions(mojo_base.mojom.String16 word) =>
+ (array<array<mojo_base.mojom.String16>> suggestions);
};
enum Decoration {
diff --git a/chromium/components/spellcheck/common/spellcheck_common.cc b/chromium/components/spellcheck/common/spellcheck_common.cc
index 82db6b998a5..4452a605421 100644
--- a/chromium/components/spellcheck/common/spellcheck_common.cc
+++ b/chromium/components/spellcheck/common/spellcheck_common.cc
@@ -111,10 +111,11 @@ base::FilePath GetVersionedFileName(base::StringPiece input_language,
// number if you're updating either dic or aff files. Increment the minor
// version number if you're updating only dic_delta files.
static constexpr LanguageVersion kSpecialVersionString[] = {
- {"tr-TR",
- "-4-0"}, // Jan 9, 2013: Add "FLAG num" to aff to avoid heapcheck
- // crash.
- {"tg-TG", "-5-0"}, // Mar 4, 2014: Add Tajik dictionary.
+ // Jan 9, 2013: Add "FLAG num" to aff to avoid heapcheck crash.
+ {"tr-TR", "-4-0"},
+
+ // Mar 4, 2014: Add Tajik dictionary.
+ {"tg-TG", "-5-0"},
// October 2017: Update from upstream.
{"en-AU", "-8-0"},
@@ -130,6 +131,10 @@ base::FilePath GetVersionedFileName(base::StringPiece input_language,
// April 2019: Update Persian
{"fa-IR", "-8-0"},
+
+ // November 2019: Update Serbian-Latin and Serbian-Cyrillic
+ {"sh", "-4-0"},
+ {"sr", "-4-0"},
};
// Generate the bdict file name using default version string or special
@@ -191,4 +196,33 @@ void GetISOLanguageCountryCodeFromLocale(const std::string& locale,
*country_code = std::string(country);
}
+void FillSuggestions(
+ const std::vector<std::vector<base::string16>>& suggestions_list,
+ std::vector<base::string16>* optional_suggestions) {
+ DCHECK(optional_suggestions);
+ size_t num_languages = suggestions_list.size();
+
+ // Compute maximum number of suggestions in a single language.
+ size_t max_suggestions = 0;
+ for (const auto& suggestions : suggestions_list)
+ max_suggestions = std::max(max_suggestions, suggestions.size());
+
+ for (size_t count = 0; count < (max_suggestions * num_languages); ++count) {
+ size_t language = count % num_languages;
+ size_t index = count / num_languages;
+
+ if (suggestions_list[language].size() <= index)
+ continue;
+
+ const base::string16& suggestion = suggestions_list[language][index];
+ // Only add the suggestion if it's unique.
+ if (!base::Contains(*optional_suggestions, suggestion)) {
+ optional_suggestions->push_back(suggestion);
+ }
+ if (optional_suggestions->size() >= kMaxSuggestions) {
+ break;
+ }
+ }
+}
+
} // namespace spellcheck
diff --git a/chromium/components/spellcheck/common/spellcheck_common.h b/chromium/components/spellcheck/common/spellcheck_common.h
index 10ab733d164..6b5428b92b5 100644
--- a/chromium/components/spellcheck/common/spellcheck_common.h
+++ b/chromium/components/spellcheck/common/spellcheck_common.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/strings/string_piece.h"
+#include "components/spellcheck/common/spellcheck_result.h"
namespace base {
class FilePath;
@@ -18,6 +19,12 @@ class FilePath;
namespace spellcheck {
+// Short type for holding spellcheck results per individual language.
+using PerLanguageResult = std::vector<std::vector<::SpellCheckResult>>;
+
+// Short type for holding word replacements per individual language.
+using PerLanguageSuggestions = std::vector<std::vector<base::string16>>;
+
// Max number of dictionary suggestions.
static const int kMaxSuggestions = 5;
@@ -54,6 +61,14 @@ void GetISOLanguageCountryCodeFromLocale(const std::string& locale,
std::string* language_code,
std::string* country_code);
+// Evenly fill |optional_suggestions| with a maximum of |kMaxSuggestions|
+// suggestions from |suggestions_list|. suggestions_list[i][j] is the j-th
+// suggestion from the i-th language's suggestions. |optional_suggestions|
+// cannot be null.
+void FillSuggestions(
+ const std::vector<std::vector<base::string16>>& suggestions_list,
+ std::vector<base::string16>* optional_suggestions);
+
} // namespace spellcheck
#endif // COMPONENTS_SPELLCHECK_COMMON_SPELLCHECK_COMMON_H_
diff --git a/chromium/components/spellcheck/common/spellcheck_features.cc b/chromium/components/spellcheck/common/spellcheck_features.cc
index 0bc03af8d4d..0732801e2f2 100644
--- a/chromium/components/spellcheck/common/spellcheck_features.cc
+++ b/chromium/components/spellcheck/common/spellcheck_features.cc
@@ -13,13 +13,10 @@ namespace spellcheck {
#if BUILDFLAG(ENABLE_SPELLCHECK)
-const base::Feature kSpellingServiceRestApi{"SpellingServiceRestApi",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
-#if defined(OS_WIN)
-const base::Feature kWinUseBrowserSpellChecker{
- "WinUseBrowserSpellChecker", base::FEATURE_DISABLED_BY_DEFAULT};
-#endif // defined(OS_WIN)
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+const base::Feature kWinUseHybridSpellChecker{
+ "WinUseHybridSpellChecker", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
bool UseBrowserSpellChecker() {
#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
@@ -33,16 +30,25 @@ bool UseBrowserSpellChecker() {
}
#if defined(OS_WIN)
+const base::Feature kWinUseBrowserSpellChecker{
+ "WinUseBrowserSpellChecker", base::FEATURE_DISABLED_BY_DEFAULT};
+
bool WindowsVersionSupportsSpellchecker() {
return base::win::GetVersion() > base::win::Version::WIN7 &&
base::win::GetVersion() < base::win::Version::WIN_LAST;
}
-#endif // defined(OS_WIN)
-#endif // BUILDFLAG(ENABLE_SPELLCHECK)
-
-#if BUILDFLAG(ENABLE_SPELLCHECK) && defined(OS_ANDROID)
+bool UseWinHybridSpellChecker() {
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ return base::FeatureList::IsEnabled(spellcheck::kWinUseHybridSpellChecker) &&
+ UseBrowserSpellChecker();
+#else
+ return false;
+#endif
+}
+#endif // defined(OS_WIN)
+#if defined(OS_ANDROID)
// Enables/disables Android spellchecker.
const base::Feature kAndroidSpellChecker{
"AndroidSpellChecker", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -62,7 +68,8 @@ bool IsAndroidSpellCheckFeatureEnabled() {
return false;
}
+#endif // defined(OS_ANDROID)
-#endif // BUILDFLAG(ENABLE_SPELLCHECK) && defined(OS_ANDROID)
+#endif // BUILDFLAG(ENABLE_SPELLCHECK)
} // namespace spellcheck
diff --git a/chromium/components/spellcheck/common/spellcheck_features.h b/chromium/components/spellcheck/common/spellcheck_features.h
index cef82f01422..ed2936f88de 100644
--- a/chromium/components/spellcheck/common/spellcheck_features.h
+++ b/chromium/components/spellcheck/common/spellcheck_features.h
@@ -12,26 +12,28 @@
namespace spellcheck {
#if BUILDFLAG(ENABLE_SPELLCHECK)
-extern const base::Feature kSpellingServiceRestApi;
-#if defined(OS_WIN)
-extern const base::Feature kWinUseBrowserSpellChecker;
-#endif // defined(OS_WIN)
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+extern const base::Feature kWinUseHybridSpellChecker;
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
bool UseBrowserSpellChecker();
#if defined(OS_WIN)
+extern const base::Feature kWinUseBrowserSpellChecker;
+
bool WindowsVersionSupportsSpellchecker();
+bool UseWinHybridSpellChecker();
#endif // defined(OS_WIN)
-#endif // BUILDFLAG(ENABLE_SPELLCHECK)
-
-#if BUILDFLAG(ENABLE_SPELLCHECK) && defined(OS_ANDROID)
+#if defined(OS_ANDROID)
extern const base::Feature kAndroidSpellChecker;
extern const base::Feature kAndroidSpellCheckerNonLowEnd;
bool IsAndroidSpellCheckFeatureEnabled();
-#endif // BUILDFLAG(ENABLE_SPELLCHECK) && defined(OS_ANDROID)
+#endif // defined(OS_ANDROID)
+
+#endif // BUILDFLAG(ENABLE_SPELLCHECK)
} // namespace spellcheck
diff --git a/chromium/components/spellcheck/renderer/spellcheck.cc b/chromium/components/spellcheck/renderer/spellcheck.cc
index f3592216cf4..647bba714b4 100644
--- a/chromium/components/spellcheck/renderer/spellcheck.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck.cc
@@ -25,8 +25,6 @@
#include "components/spellcheck/renderer/spellcheck_language.h"
#include "components/spellcheck/renderer/spellcheck_provider.h"
#include "components/spellcheck/spellcheck_buildflags.h"
-#include "content/public/common/service_manager_connection.h"
-#include "content/public/common/simple_connection_filter.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_visitor.h"
#include "content/public/renderer/render_thread.h"
@@ -154,50 +152,14 @@ class SpellCheck::SpellcheckRequest {
// values.
// TODO(groby): Simplify this.
SpellCheck::SpellCheck(
- service_manager::BinderRegistry* registry,
service_manager::LocalInterfaceProvider* embedder_provider)
: embedder_provider_(embedder_provider), spellcheck_enabled_(true) {
DCHECK(embedder_provider);
- if (!registry)
- return; // Can be NULL in tests.
- registry->AddInterface(base::BindRepeating(&SpellCheck::SpellCheckerReceiver,
- weak_factory_.GetWeakPtr()),
- base::ThreadTaskRunnerHandle::Get());
}
-SpellCheck::~SpellCheck() {
-}
-
-void SpellCheck::FillSuggestions(
- const std::vector<std::vector<base::string16>>& suggestions_list,
- std::vector<base::string16>* optional_suggestions) {
- DCHECK(optional_suggestions);
- size_t num_languages = suggestions_list.size();
-
- // Compute maximum number of suggestions in a single language.
- size_t max_suggestions = 0;
- for (const auto& suggestions : suggestions_list)
- max_suggestions = std::max(max_suggestions, suggestions.size());
-
- for (size_t count = 0; count < (max_suggestions * num_languages); ++count) {
- size_t language = count % num_languages;
- size_t index = count / num_languages;
-
- if (suggestions_list[language].size() <= index)
- continue;
+SpellCheck::~SpellCheck() = default;
- const base::string16& suggestion = suggestions_list[language][index];
- // Only add the suggestion if it's unique.
- if (!base::Contains(*optional_suggestions, suggestion)) {
- optional_suggestions->push_back(suggestion);
- }
- if (optional_suggestions->size() >= spellcheck::kMaxSuggestions) {
- break;
- }
- }
-}
-
-void SpellCheck::SpellCheckerReceiver(
+void SpellCheck::BindReceiver(
mojo::PendingReceiver<spellcheck::mojom::SpellChecker> receiver) {
receivers_.Add(this, std::move(receiver));
}
@@ -242,6 +204,19 @@ void SpellCheck::AddSpellcheckLanguage(base::File file,
languages_.back()->Init(std::move(file), language);
}
+bool SpellCheck::SpellCheckWord(const base::char16* text_begin,
+ size_t position_in_text,
+ size_t text_length,
+ int tag,
+ size_t* misspelling_start,
+ size_t* misspelling_len,
+ std::nullptr_t null_suggestions_ptr) {
+ return SpellCheckWord(
+ text_begin, position_in_text, text_length, tag, misspelling_start,
+ misspelling_len,
+ static_cast<spellcheck::PerLanguageSuggestions*>(nullptr));
+}
+
bool SpellCheck::SpellCheckWord(
const base::char16* text_begin,
size_t position_in_text,
@@ -250,6 +225,29 @@ bool SpellCheck::SpellCheckWord(
size_t* misspelling_start,
size_t* misspelling_len,
std::vector<base::string16>* optional_suggestions) {
+ if (!optional_suggestions) {
+ return SpellCheckWord(text_begin, position_in_text, text_length, tag,
+ misspelling_start, misspelling_len, nullptr);
+ }
+
+ bool result;
+ spellcheck::PerLanguageSuggestions per_language_suggestions;
+ result = SpellCheckWord(text_begin, position_in_text, text_length, tag,
+ misspelling_start, misspelling_len,
+ &per_language_suggestions);
+ spellcheck::FillSuggestions(per_language_suggestions, optional_suggestions);
+
+ return result;
+}
+
+bool SpellCheck::SpellCheckWord(
+ const base::char16* text_begin,
+ size_t position_in_text,
+ size_t text_length,
+ int tag,
+ size_t* misspelling_start,
+ size_t* misspelling_len,
+ spellcheck::PerLanguageSuggestions* optional_per_language_suggestions) {
DCHECK(text_length >= position_in_text);
DCHECK(misspelling_start && misspelling_len) << "Out vars must be given.";
@@ -258,6 +256,13 @@ bool SpellCheck::SpellCheckWord(
if (InitializeIfNeeded())
return true;
+ // To prevent an infinite loop below, ensure that at least one language is
+ // enabled before starting the check. If no language is enabled, we should
+ // never report a spelling mistake, so return true here.
+ if (EnabledLanguageCount() == 0) {
+ return true;
+ }
+
// These are for holding misspelling or skippable word positions and lengths
// between calls to SpellcheckLanguage::SpellCheckWord.
size_t possible_misspelling_start;
@@ -283,12 +288,24 @@ bool SpellCheck::SpellCheckWord(
suggestions_list.clear();
for (auto language = languages_.begin(); language != languages_.end();) {
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ if (!(*language)->IsEnabled()) {
+ // In the case of hybrid spell checking on Windows, languages that are
+ // handled on the browser side are marked as disabled on the renderer
+ // side. We do not want to return IS_CORRECT for those languages, so we
+ // simply skip them.
+ language++;
+ continue;
+ }
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
language_suggestions.clear();
SpellcheckLanguage::SpellcheckWordResult result =
(*language)->SpellCheckWord(
text_begin, position_in_text, text_length, tag,
&possible_misspelling_start, &possible_misspelling_len,
- optional_suggestions ? &language_suggestions : nullptr);
+ optional_per_language_suggestions ? &language_suggestions
+ : nullptr);
switch (result) {
case SpellcheckLanguage::SpellcheckWordResult::IS_CORRECT:
@@ -331,8 +348,9 @@ bool SpellCheck::SpellCheckWord(
// If |*misspelling_len| is non-zero, that means at least one language
// marked a word misspelled and no other language considered it correct.
if (*misspelling_len != 0) {
- if (optional_suggestions)
- FillSuggestions(suggestions_list, optional_suggestions);
+ if (optional_per_language_suggestions) {
+ optional_per_language_suggestions->swap(suggestions_list);
+ }
return false;
}
}
@@ -341,51 +359,69 @@ bool SpellCheck::SpellCheckWord(
return true;
}
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+void SpellCheck::HybridSpellCheckParagraph(
+ const base::string16& text,
+ SpellCheck::RendererTextCheckCallback callback) {
+ // Count how many languages need to be checked on the renderer side, and
+ // skip the Hunspell check if the result is 0.
+ size_t renderer_languages_count = EnabledLanguageCount();
+ std::vector<SpellCheckResult> renderer_results;
+
+ if (renderer_languages_count > 0) {
+ WebVector<blink::WebTextCheckingResult> web_results;
+ SpellCheckParagraph(text, &web_results);
+
+ // Convert the results to non-blink format.
+ renderer_results.resize(web_results.size());
+ std::transform(web_results.begin(), web_results.end(),
+ renderer_results.begin(),
+ [](const WebTextCheckingResult& result) {
+ return SpellCheckResult(SpellCheckResult::SPELLING,
+ result.location, result.length);
+ });
+ }
+
+ std::move(callback).Run(std::move(renderer_results));
+}
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
+#if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
bool SpellCheck::SpellCheckParagraph(
const base::string16& text,
WebVector<WebTextCheckingResult>* results) {
-#if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
- if (!spellcheck::UseBrowserSpellChecker()) {
- DCHECK(results);
- std::vector<WebTextCheckingResult> textcheck_results;
- size_t length = text.length();
- size_t position_in_text = 0;
-
- // Spellcheck::SpellCheckWord() automatically breaks text into words and
- // checks the spellings of the extracted words. This function sets the
- // position and length of the first misspelled word and returns false when
- // the text includes misspelled words. Therefore, we just repeat calling the
- // function until it returns true to check the whole text.
- size_t misspelling_start = 0;
- size_t misspelling_length = 0;
- while (position_in_text <= length) {
- if (SpellCheckWord(text.c_str(), position_in_text, length, kNoTag,
- &misspelling_start, &misspelling_length, nullptr)) {
- results->Assign(textcheck_results);
- return true;
- }
+ DCHECK(results);
+ std::vector<WebTextCheckingResult> textcheck_results;
+ size_t length = text.length();
+ size_t position_in_text = 0;
+
+ // Spellcheck::SpellCheckWord() automatically breaks text into words and
+ // checks the spellings of the extracted words. This function sets the
+ // position and length of the first misspelled word and returns false when
+ // the text includes misspelled words. Therefore, we just repeat calling the
+ // function until it returns true to check the whole text.
+ size_t misspelling_start = 0;
+ size_t misspelling_length = 0;
+ while (position_in_text <= length) {
+ if (SpellCheckWord(text.c_str(), position_in_text, length, kNoTag,
+ &misspelling_start, &misspelling_length, nullptr)) {
+ results->Assign(textcheck_results);
+ return true;
+ }
- if (!custom_dictionary_.SpellCheckWord(text, misspelling_start,
- misspelling_length)) {
- textcheck_results.push_back(
- WebTextCheckingResult(blink::kWebTextDecorationTypeSpelling,
- base::checked_cast<int>(misspelling_start),
- base::checked_cast<int>(misspelling_length)));
- }
- position_in_text = misspelling_start + misspelling_length;
+ if (!custom_dictionary_.SpellCheckWord(text, misspelling_start,
+ misspelling_length)) {
+ textcheck_results.push_back(
+ WebTextCheckingResult(blink::kWebTextDecorationTypeSpelling,
+ base::checked_cast<int>(misspelling_start),
+ base::checked_cast<int>(misspelling_length)));
}
- results->Assign(textcheck_results);
- return false;
+ position_in_text = misspelling_start + misspelling_length;
}
-#endif
-
- // This function is only invoked if renderer(hunspell) spellchecker is used.
- DCHECK(spellcheck::UseBrowserSpellChecker());
- NOTREACHED();
- return true;
+ results->Assign(textcheck_results);
+ return false;
}
-#if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
void SpellCheck::RequestTextChecking(
const base::string16& text,
std::unique_ptr<blink::WebTextCheckingCompletion> completion) {
@@ -521,6 +557,17 @@ void SpellCheck::RemoveDictionaryUpdateObserver(
return dictionary_update_observers_.RemoveObserver(observer);
}
+size_t SpellCheck::LanguageCount() {
+ return languages_.size();
+}
+
+size_t SpellCheck::EnabledLanguageCount() {
+ return std::count_if(languages_.begin(), languages_.end(),
+ [](std::unique_ptr<SpellcheckLanguage>& language) {
+ return language->IsEnabled();
+ });
+}
+
void SpellCheck::NotifyDictionaryObservers(
const WebVector<WebString>& words_added) {
for (auto& observer : dictionary_update_observers_) {
diff --git a/chromium/components/spellcheck/renderer/spellcheck.h b/chromium/components/spellcheck/renderer/spellcheck.h
index b9fe4287e14..9129ed03133 100644
--- a/chromium/components/spellcheck/renderer/spellcheck.h
+++ b/chromium/components/spellcheck/renderer/spellcheck.h
@@ -17,11 +17,11 @@
#include "base/observer_list.h"
#include "base/strings/string16.h"
#include "components/spellcheck/common/spellcheck.mojom.h"
+#include "components/spellcheck/common/spellcheck_common.h"
#include "components/spellcheck/renderer/custom_dictionary_engine.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
class SpellcheckLanguage;
struct SpellCheckResult;
@@ -53,6 +53,9 @@ class DictionaryUpdateObserver {
class SpellCheck : public base::SupportsWeakPtr<SpellCheck>,
public spellcheck::mojom::SpellChecker {
public:
+ using RendererTextCheckCallback =
+ base::OnceCallback<void(std::vector<SpellCheckResult>)>;
+
// TODO(groby): I wonder if this can be private, non-mac only.
class SpellcheckRequest;
enum ResultFilter {
@@ -60,8 +63,8 @@ class SpellCheck : public base::SupportsWeakPtr<SpellCheck>,
USE_NATIVE_CHECKER, // Use native checker to double-check.
};
- SpellCheck(service_manager::BinderRegistry* registry,
- service_manager::LocalInterfaceProvider* embedder_provider);
+ explicit SpellCheck(
+ service_manager::LocalInterfaceProvider* embedder_provider);
~SpellCheck() override;
void AddSpellcheckLanguage(base::File file, const std::string& language);
@@ -94,6 +97,38 @@ class SpellCheck : public base::SupportsWeakPtr<SpellCheck>,
size_t* misspelling_len,
std::vector<base::string16>* optional_suggestions);
+ // Overload of SpellCheckWord where the replacement suggestions are kept
+ // separately per language, instead of combined into a single list. This is
+ // useful if the suggestions must be merged with another list of suggestions,
+ // for example in the case of the Windows hybrid spellchecker.
+ bool SpellCheckWord(
+ const base::char16* text_begin,
+ size_t position_in_text,
+ size_t text_length,
+ int tag,
+ size_t* misspelling_start,
+ size_t* misspelling_len,
+ spellcheck::PerLanguageSuggestions* optional_per_language_suggestions);
+
+ // Overload of SpellCheckWord for skipping optional suggestions with a
+ // nullptr, used to disambiguate between the other two overloads.
+ bool SpellCheckWord(const base::char16* text_begin,
+ size_t position_in_text,
+ size_t text_length,
+ int tag,
+ size_t* misspelling_start,
+ size_t* misspelling_len,
+ std::nullptr_t null_suggestions_ptr);
+
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ // Like SpellCheckParagraph, but only checks languages that the native checker
+ // can't handle.
+ // This method uses non-blink classes for the spellcheck results.
+ void HybridSpellCheckParagraph(const base::string16& text,
+ RendererTextCheckCallback callback);
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
+#if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
// SpellCheck a paragraph.
// Returns true if |text| is correctly spelled, false otherwise.
// If the spellchecker failed to initialize, always returns true.
@@ -101,7 +136,6 @@ class SpellCheck : public base::SupportsWeakPtr<SpellCheck>,
const base::string16& text,
blink::WebVector<blink::WebTextCheckingResult>* results);
-#if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
// Requests to spellcheck the specified text in the background. This function
// posts a background task and calls SpellCheckParagraph() in the task.
void RequestTextChecking(
@@ -127,24 +161,22 @@ class SpellCheck : public base::SupportsWeakPtr<SpellCheck>,
// Remove observer on dictionary update event.
void RemoveDictionaryUpdateObserver(DictionaryUpdateObserver* observer);
+ // Binds receivers for the SpellChecker interface.
+ void BindReceiver(
+ mojo::PendingReceiver<spellcheck::mojom::SpellChecker> receiver);
+
+ // Returns the current number of spell check languages.
+ size_t LanguageCount();
+
+ // Returns the current number of spell check languages with enabled engines.
+ size_t EnabledLanguageCount();
+
private:
friend class SpellCheckTest;
FRIEND_TEST_ALL_PREFIXES(SpellCheckTest, GetAutoCorrectionWord_EN_US);
FRIEND_TEST_ALL_PREFIXES(SpellCheckTest,
RequestSpellCheckMultipleTimesWithoutInitialization);
- // Evenly fill |optional_suggestions| with a maximum of |kMaxSuggestions|
- // suggestions from |suggestions_list|. suggestions_list[i][j] is the j-th
- // suggestion from the i-th language's suggestions. |optional_suggestions|
- // cannot be null.
- static void FillSuggestions(
- const std::vector<std::vector<base::string16>>& suggestions_list,
- std::vector<base::string16>* optional_suggestions);
-
- // Binds receivers for the SpellChecker interface.
- void SpellCheckerReceiver(
- mojo::PendingReceiver<spellcheck::mojom::SpellChecker> receiver);
-
// spellcheck::mojom::SpellChecker:
void Initialize(
std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
diff --git a/chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc b/chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc
index 4c333abf366..775c90f201c 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_multilingual_unittest.cc
@@ -52,7 +52,7 @@ class MultilingualSpellCheckTest : public testing::Test {
MultilingualSpellCheckTest() {}
void ReinitializeSpellCheck(const std::string& unsplit_languages) {
- spellcheck_ = new SpellCheck(nullptr, &embedder_provider_);
+ spellcheck_ = new SpellCheck(&embedder_provider_);
provider_.reset(
new TestingSpellCheckProvider(spellcheck_, &embedder_provider_));
InitializeSpellCheck(unsplit_languages);
diff --git a/chromium/components/spellcheck/renderer/spellcheck_panel.cc b/chromium/components/spellcheck/renderer/spellcheck_panel.cc
index cdaf25a577f..e5e06984469 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_panel.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_panel.cc
@@ -72,9 +72,10 @@ void SpellCheckPanel::ToggleSpellPanel(bool visible) {
blink::WebString::FromUTF8("ToggleSpellPanel"));
}
-spellcheck::mojom::SpellCheckPanelHostPtr
+mojo::Remote<spellcheck::mojom::SpellCheckPanelHost>
SpellCheckPanel::GetSpellCheckPanelHost() {
- spellcheck::mojom::SpellCheckPanelHostPtr spell_check_panel_host;
- embedder_provider_->GetInterface(&spell_check_panel_host);
+ mojo::Remote<spellcheck::mojom::SpellCheckPanelHost> spell_check_panel_host;
+ embedder_provider_->GetInterface(
+ spell_check_panel_host.BindNewPipeAndPassReceiver());
return spell_check_panel_host;
}
diff --git a/chromium/components/spellcheck/renderer/spellcheck_panel.h b/chromium/components/spellcheck/renderer/spellcheck_panel.h
index b6e451662b4..5565b404cc3 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_panel.h
+++ b/chromium/components/spellcheck/renderer/spellcheck_panel.h
@@ -11,6 +11,7 @@
#include "content/public/renderer/render_frame_observer.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "third_party/blink/public/platform/web_spell_check_panel_host_client.h"
@@ -49,7 +50,7 @@ class SpellCheckPanel : public content::RenderFrameObserver,
void ToggleSpellPanel(bool visible) override;
void AdvanceToNextMisspelling() override;
- spellcheck::mojom::SpellCheckPanelHostPtr GetSpellCheckPanelHost();
+ mojo::Remote<spellcheck::mojom::SpellCheckPanelHost> GetSpellCheckPanelHost();
// SpellCheckPanel receivers.
mojo::ReceiverSet<spellcheck::mojom::SpellCheckPanel> receivers_;
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider.cc b/chromium/components/spellcheck/renderer/spellcheck_provider.cc
index f6644b07b0b..a82808a2ca9 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider.cc
@@ -8,6 +8,7 @@
#include "base/metrics/histogram_macros.h"
#include "build/build_config.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/renderer/spellcheck.h"
@@ -83,7 +84,6 @@ SpellCheckProvider::SpellCheckProvider(
SpellCheck* spellcheck,
service_manager::LocalInterfaceProvider* embedder_provider)
: content::RenderFrameObserver(render_frame),
- content::RenderFrameObserverTracker<SpellCheckProvider>(render_frame),
spellcheck_(spellcheck),
embedder_provider_(embedder_provider) {
DCHECK(spellcheck_);
@@ -111,6 +111,40 @@ spellcheck::mojom::SpellCheckHost& SpellCheckProvider::GetSpellCheckHost() {
return *spell_check_host_;
}
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+void SpellCheckProvider::HybridSpellCheckParagraphComplete(
+ const base::string16& text,
+ const int request_id,
+ std::vector<SpellCheckResult> renderer_results) {
+ size_t renderer_languages = spellcheck_->EnabledLanguageCount();
+
+ // If there are no renderer results, either 1) there were no languages to
+ // check on the renderer side because all languages are supported by the
+ // platform; or 2) each word was marked correct by at least 1 language. In
+ // case of 2), we can skip the platform check, because we know all words are
+ // correctly spelled in at least 1 language.
+ // Additionally, if the renderer checked all languages, we can completely skip
+ // the browser side.
+ if ((renderer_results.empty() && renderer_languages > 0) ||
+ renderer_languages == spellcheck_->LanguageCount()) {
+ OnRespondTextCheck(request_id, text, renderer_results);
+ } else {
+ // We need to do a platform check. We must decide whether to find the
+ // replacement suggestions now, or wait until the user requests them. If all
+ // languages are checked by the platform, find the suggestions now, because
+ // the platform spellchecker is fast. If we had to check some languages via
+ // Hunspell, then wait until the user requests the replacements, because
+ // Hunspell is extremely slow at finding them and can hang the page for a
+ // few seconds if the text is not small.
+ GetSpellCheckHost().RequestPartialTextCheck(
+ text, routing_id(), std::move(renderer_results),
+ renderer_languages == 0,
+ base::BindOnce(&SpellCheckProvider::OnRespondTextCheck,
+ weak_factory_.GetWeakPtr(), request_id, text));
+ }
+}
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
void SpellCheckProvider::RequestTextChecking(
const base::string16& text,
std::unique_ptr<WebTextCheckingCompletion> completion) {
@@ -130,6 +164,22 @@ void SpellCheckProvider::RequestTextChecking(
last_results_.Assign(blink::WebVector<blink::WebTextCheckingResult>());
last_identifier_ = text_check_completions_.Add(std::move(completion));
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ if (spellcheck::UseWinHybridSpellChecker()) {
+ // Do a first spellcheck pass with Hunspell, then check the rest of the
+ // locales with the native spellchecker.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &SpellCheck::HybridSpellCheckParagraph, spellcheck_->AsWeakPtr(),
+ text,
+ base::BindOnce(
+ &SpellCheckProvider::HybridSpellCheckParagraphComplete,
+ weak_factory_.GetWeakPtr(), text, last_identifier_)));
+ return;
+ }
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
if (spellcheck::UseBrowserSpellChecker()) {
// Text check (unified request for grammar and spell check) is only
@@ -141,6 +191,7 @@ void SpellCheckProvider::RequestTextChecking(
weak_factory_.GetWeakPtr(), last_identifier_, text));
}
#endif
+
#if BUILDFLAG(USE_RENDERER_SPELLCHECKER)
if (!spellcheck::UseBrowserSpellChecker()) {
GetSpellCheckHost().CallSpellingService(
@@ -177,12 +228,32 @@ void SpellCheckProvider::CheckSpelling(
size_t& length,
WebVector<WebString>* optional_suggestions) {
base::string16 word = text.Utf16();
- std::vector<base::string16> suggestions;
const int kWordStart = 0;
- spellcheck_->SpellCheckWord(word.c_str(), kWordStart, word.size(),
- routing_id(), &offset, &length,
- optional_suggestions ? &suggestions : nullptr);
+
if (optional_suggestions) {
+ spellcheck::PerLanguageSuggestions per_language_suggestions;
+ spellcheck_->SpellCheckWord(word.c_str(), kWordStart, word.size(),
+ routing_id(), &offset, &length,
+ &per_language_suggestions);
+
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ if (spellcheck::UseWinHybridSpellChecker()) {
+ // Also fetch suggestions from the browser process (native spellchecker).
+ // This is a synchronous Mojo call, because this method must return
+ // synchronously.
+ spellcheck::PerLanguageSuggestions browser_suggestions;
+ GetSpellCheckHost().GetPerLanguageSuggestions(word, &browser_suggestions);
+
+ per_language_suggestions.reserve(per_language_suggestions.size() +
+ browser_suggestions.size());
+ per_language_suggestions.insert(per_language_suggestions.end(),
+ browser_suggestions.begin(),
+ browser_suggestions.end());
+ }
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
+ std::vector<base::string16> suggestions;
+ spellcheck::FillSuggestions(per_language_suggestions, &suggestions);
WebVector<WebString> web_suggestions(suggestions.size());
std::transform(
suggestions.begin(), suggestions.end(), web_suggestions.begin(),
@@ -191,10 +262,13 @@ void SpellCheckProvider::CheckSpelling(
UMA_HISTOGRAM_COUNTS_1M("SpellCheck.api.check.suggestions",
base::saturated_cast<int>(word.size()));
} else {
+ spellcheck_->SpellCheckWord(word.c_str(), kWordStart, word.size(),
+ routing_id(), &offset, &length,
+ /* optional suggestions vector */ nullptr);
UMA_HISTOGRAM_COUNTS_1M("SpellCheck.api.check",
base::saturated_cast<int>(word.size()));
// If optional_suggestions is not requested, the API is called
- // for marking. So we use this for counting markable words.
+ // for marking. So we use this for counting markable words.
GetSpellCheckHost().NotifyChecked(word, 0 < length);
}
}
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider.h b/chromium/components/spellcheck/renderer/spellcheck_provider.h
index a473f476198..f6542262fab 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider.h
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider.h
@@ -13,7 +13,6 @@
#include "components/spellcheck/common/spellcheck.mojom.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "content/public/renderer/render_frame_observer.h"
-#include "content/public/renderer/render_frame_observer_tracker.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/web/web_text_check_client.h"
@@ -32,10 +31,8 @@ class LocalInterfaceProvider;
// This class deals with asynchronously invoking text spelling and grammar
// checking services provided by the browser process (host).
-class SpellCheckProvider
- : public content::RenderFrameObserver,
- public content::RenderFrameObserverTracker<SpellCheckProvider>,
- public blink::WebTextCheckClient {
+class SpellCheckProvider : public content::RenderFrameObserver,
+ public blink::WebTextCheckClient {
public:
using WebTextCheckCompletions =
base::IDMap<std::unique_ptr<blink::WebTextCheckingCompletion>>;
@@ -119,6 +116,13 @@ class SpellCheckProvider
const std::vector<SpellCheckResult>& results);
#endif
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ void HybridSpellCheckParagraphComplete(
+ const base::string16& text,
+ const int request_id,
+ std::vector<SpellCheckResult> renderer_results);
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
// Holds ongoing spellchecking operations.
WebTextCheckCompletions text_check_completions_;
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider_test.cc b/chromium/components/spellcheck/renderer/spellcheck_provider_test.cc
index d8e106654bc..b441ce4397d 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider_test.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider_test.cc
@@ -33,7 +33,7 @@ void FakeTextCheckingCompletion::DidCancelCheckingText() {
TestingSpellCheckProvider::TestingSpellCheckProvider(
service_manager::LocalInterfaceProvider* embedder_provider)
: SpellCheckProvider(nullptr,
- new SpellCheck(nullptr, embedder_provider),
+ new SpellCheck(embedder_provider),
embedder_provider) {}
TestingSpellCheckProvider::TestingSpellCheckProvider(
@@ -117,6 +117,23 @@ void TestingSpellCheckProvider::FillSuggestionList(const base::string16&,
}
#endif // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+void TestingSpellCheckProvider::GetPerLanguageSuggestions(
+ const base::string16& word,
+ GetPerLanguageSuggestionsCallback callback) {
+ NOTREACHED();
+}
+
+void TestingSpellCheckProvider::RequestPartialTextCheck(
+ const base::string16& text,
+ int route_id,
+ const std::vector<SpellCheckResult>& partial_results,
+ bool fill_suggestions,
+ RequestPartialTextCheckCallback callback) {
+ NOTREACHED();
+}
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
#if defined(OS_ANDROID)
void TestingSpellCheckProvider::DisconnectSessionBridge() {
NOTREACHED();
diff --git a/chromium/components/spellcheck/renderer/spellcheck_provider_test.h b/chromium/components/spellcheck/renderer/spellcheck_provider_test.h
index d97e8b8375c..396e2e434cf 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_provider_test.h
+++ b/chromium/components/spellcheck/renderer/spellcheck_provider_test.h
@@ -102,6 +102,18 @@ class TestingSpellCheckProvider : public SpellCheckProvider,
FillSuggestionListCallback) override;
#endif
+#if BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+ void GetPerLanguageSuggestions(
+ const base::string16& word,
+ GetPerLanguageSuggestionsCallback callback) override;
+ void RequestPartialTextCheck(
+ const base::string16& text,
+ int route_id,
+ const std::vector<SpellCheckResult>& partial_results,
+ bool fill_suggestions,
+ RequestPartialTextCheckCallback callback) override;
+#endif // BUILDFLAG(USE_WIN_HYBRID_SPELLCHECKER)
+
#if defined(OS_ANDROID)
void DisconnectSessionBridge() override;
#endif
diff --git a/chromium/components/spellcheck/renderer/spellcheck_unittest.cc b/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
index cf560670fd0..4fdf8c40fc2 100644
--- a/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
+++ b/chromium/components/spellcheck/renderer/spellcheck_unittest.cc
@@ -60,7 +60,7 @@ class SpellCheckTest : public testing::Test {
}
void UninitializeSpellCheck() {
- spell_check_ = std::make_unique<SpellCheck>(nullptr, &embedder_provider_);
+ spell_check_ = std::make_unique<SpellCheck>(&embedder_provider_);
}
bool InitializeIfNeeded() {
@@ -106,7 +106,7 @@ class SpellCheckTest : public testing::Test {
static void FillSuggestions(
const std::vector<std::vector<base::string16>>& suggestions_list,
std::vector<base::string16>* optional_suggestions) {
- SpellCheck::FillSuggestions(suggestions_list, optional_suggestions);
+ spellcheck::FillSuggestions(suggestions_list, optional_suggestions);
}
#if !defined(OS_MACOSX)
@@ -736,21 +736,20 @@ TEST_F(SpellCheckTest, SpellCheckText) {
}, {
// Serbo-Croatian (Serbian Latin)
"sh",
- L"Google-ova misija je da organizuje sve informacije na svetu i "
- L"u\x010dini ih univerzal-no dostupnim i korisnim."
+ L"Guglova misija je organizirati svjetske informacije i u\x010diniti ih "
+ L"univerzalno dostupnim i korisnim."
}, {
// Serbian
"sr",
- L"\x0047\x006f\x006f\x0067\x006c\x0065\x002d\x043e\x0432\x0430 "
- L"\x043c\x0438\x0441\x0438\x0458\x0430 \x0458\x0435 \x0434\x0430 "
- L"\x043e\x0440\x0433\x0430\x043d\x0438\x0437\x0443\x0458\x0435 "
- L"\x0441\x0432\x0435 "
- L"\x0438\x043d\x0444\x043e\x0440\x043c\x0430\x0446\x0438\x0458\x0435 "
- L"\x043d\x0430 \x0441\x0432\x0435\x0442\x0443 \x0438 "
- L"\x0443\x0447\x0438\x043d\x0438 \x0438\x0445 "
- L"\x0443\x043d\x0438\x0432\x0435\x0440\x0437\x0430\x043b\x043d\x043e "
- L"\x0434\x043e\x0441\x0442\x0443\x043f\x043d\x0438\x043c \x0438 "
- L"\x043a\x043e\x0440\x0438\x0441\x043d\x0438\x043c."
+ L"\x0413\x0443\x0433\x043B\x043E\x0432\x0430 "
+ L"\x043C\x0438\x0441\x0438\x0458\x0430 \x0458\x0435 \x0434\x0430 "
+ L"\x043E\x0440\x0433\x0430\x043D\x0438\x0437\x0443\x0458\x0435 "
+ L"\x0441\x0432\x0435\x0442\x0441\x043A\x0435 "
+ L"\x0438\x043D\x0444\x043E\x0440\x043C\x0430\x0446\x0438\x0458\x0435 "
+ L"\x0438 \x0443\x0447\x0438\x043D\x0438 \x0438\x0445 "
+ L"\x0443\x043D\x0438\x0432\x0435\x0440\x0437\x0430\x043B\x043D\x0438"
+ L"\x043C \x0434\x043E\x0441\x0442\x0443\x043F\x043D\x0438\x043C \x0438 "
+ L"\x043A\x043E\x0440\x0438\x0441\x043D\x0438\x043C."
}, {
// Slovak
"sk-SK",
diff --git a/chromium/components/spellcheck/renderer/spelling_engine.cc b/chromium/components/spellcheck/renderer/spelling_engine.cc
index b4485f18179..2eb52e17d12 100644
--- a/chromium/components/spellcheck/renderer/spelling_engine.cc
+++ b/chromium/components/spellcheck/renderer/spelling_engine.cc
@@ -21,8 +21,24 @@ SpellingEngine* CreateNativeSpellingEngine(
DCHECK(embedder_provider);
#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
#if defined(OS_WIN)
- if (spellcheck::UseBrowserSpellChecker())
+ if (spellcheck::UseWinHybridSpellChecker()) {
+ // On Windows, when using the hybrid spellchecker, both Hunspell and the
+ // platform are used for spellchecking. Ideally we'd want to know which
+ // languages are supported by the platform spellchecker, and only create a
+ // HunspellEngine for languages that aren't. Unfortunately, performing that
+ // check must be done asynchronously on the browser side, while this
+ // function must return synchronously. However, because the platform
+ // spellchecker uses a code path that does not involve a SpellingEngine when
+ // performing spellchecking, a solution is to create a HunspellEngine for
+ // every language, even those that the platform supports. Because the
+ // languages that the platform spellchecker supports are initialized on the
+ // browser side, the HunspellEngine returned here will remain disabled for
+ // these languages, so they will be skipped when performing the Hunspell
+ // check.
+ return new HunspellEngine(embedder_provider);
+ } else if (spellcheck::UseBrowserSpellChecker()) {
return new PlatformSpellingEngine(embedder_provider);
+ }
#else
return new PlatformSpellingEngine(embedder_provider);
#endif // defined(OS_WIN)
diff --git a/chromium/components/spellcheck/spellcheck.md b/chromium/components/spellcheck/spellcheck.md
index 2a43b86fd47..59609e798c2 100644
--- a/chromium/components/spellcheck/spellcheck.md
+++ b/chromium/components/spellcheck/spellcheck.md
@@ -1,25 +1,48 @@
# About the 'use_browser_spellchecker' and 'use_renderer_spellchecker' build time flag.
## use_browser_spellchecker
-
-Use the operating system's spellchecker rather than hunspell. This does
+Use the operating system's spellchecker rather than Hunspell. This does
not affect the "red underline" spellchecker which can consult Google's
server-based spellcheck service.
## use_renderer_spellchecker
Use hunspell spellchecker rather than the operating system's spellchecker.
-## Note:
+## Note on Windows:
For most operating system except Windows, the decision to use the platform
-spellchecker or hunspell spellchecker is made at build time. Therefore,
+spellchecker or Hunspell spellchecker is made at build time. Therefore,
use_browser_spellchecker and use_renderer_spellchecker are mutually
exclusive for most operating systems except Windows.
-For Windows OS, the decision to use the platform spellchecker or hunspell
+For Windows OS, the decision to use the platform spellchecker or Hunspell
spellchecker is made during runtime. Therefore, we include both build
flags if the platform is Windows.
We also need to create the runtime feature flag kWinUseBrowserSpellChecker
for Windows OS. The feature flag is used to choose between the platform or
-hunspell spellchecker at runtime.
+Hunspell spellchecker at runtime.
+
+## Note on the Windows Hybrid spellchecker
+On Windows, the platform spell checker relies on user-installed language packs.
+It is possible for a user to enable spell checking in Chrome for a language
+while not having installed the necessary language pack in the Windows settings.
+
+For those situations, the spell checker on Windows will first try to use the
+platform spell checker. For languages where this is not possible, Hunspell will
+be used instead.
+
+To control this fallback logic, a separate build flag
+use_win_hybrid_spellchecker is used. In addition, for experiment purposes, a
+feature flag kWinUseHybridSpellChecker is used to control this behavior at
+runtime. Finally, this fallback logic requires the platform spell checker to be
+available, so it also uses the browser spell checker checks described above.
+
+## Note on use_windows_preferred_languages_for_spellcheck
+TODO(https://crbug.com/1000443): Use Windows preferred languages to help
+populate spellcheck settings.
+
+Use union of Chromium preferred languages (from Language settings) and
+the Windows preferred languages (from system language settings) as the
+source of the spellcheck languages in the "Spell check" section of the
+Chromium language settings, provided they are supported for spellcheck.
diff --git a/chromium/components/spellcheck/spellcheck_build_features.gni b/chromium/components/spellcheck/spellcheck_build_features.gni
index 2f79dfeab49..860ff92d7dc 100644
--- a/chromium/components/spellcheck/spellcheck_build_features.gni
+++ b/chromium/components/spellcheck/spellcheck_build_features.gni
@@ -10,9 +10,28 @@ enable_spellcheck = !is_ios
# server-based spellcheck service.
use_browser_spellchecker = is_android || is_mac || is_win
-# Use hunspell. Windows is switching between OS's spellchecker and hunspell.
+# Use Hunspell. Windows can use both the OS's spellchecker and Hunspell.
# Therefore, include Windows in both build flags.
use_renderer_spellchecker = !use_browser_spellchecker || is_win
+# Use both Hunspell and the OS's spellchecker. Try to use the OS, but if
+# a locale is not supported (i.e. no Windows language pack installed by
+# the user), fall back to Hunspell. Requires Windows, and requires both
+# use_browser_spellchecker and use_renderer_spellchecker.
+use_win_hybrid_spellchecker =
+ is_win && use_browser_spellchecker && use_renderer_spellchecker
+
+# TODO(https://crbug.com/1000443): Use Windows preferred languages to help populate spellcheck settings.
+# Use union of Chromium preferred languages (from Language settings) and
+# the Windows preferred languages (from system language settings) as the
+# source of the spellcheck languages in the "Spell check" section of the
+# Chromium language settings, provided they are supported for spellcheck.
+use_windows_preferred_languages_for_spellcheck =
+ use_browser_spellchecker && is_win
+
+# Whether the enhanced spellcheck service is available on the platform. This is
+# effectively equal to all desktop platforms.
+enable_spelling_service = use_renderer_spellchecker || is_mac
+
# Only Mac has a spellcheck panel.
has_spellcheck_panel = is_mac