Repository: electron/electron (language: cpp)

Issue:
Empty dictionarySuggestions on Windows
### Preflight Checklist

- [x] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [x] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [x] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

v35.0.0-nightly.20250113

### What operating system(s) are you using?

Windows

### Operating System Version

Windows 11

### What arch are you using?

x64

### Last Known Working Electron version

v35.0.0-nightly.20250109

### Expected Behavior

Context menu contains `dictionarySuggestions`

### Actual Behavior

Context menu contains `dictionarySuggestions` without any suggestions

### Testcase Gist URL

https://gist.github.com/samuelmaddock/e66dde2361de95f3c48f443a8abdd099

### Additional Information

Regressed by https://chromium-review.googlesource.com/c/chromium/src/+/6109477 in https://github.com/electron/electron/pull/45055

Related:
* https://github.com/electron/electron/pull/29690
* https://github.com/electron/electron/issues/29685


Discussion / hints:

Fix patch:
diff --git a/patches/chromium/.patches b/patches/chromium/.patches
index fec7148a7ec5a..cea4da74f5f7f 100644
--- a/patches/chromium/.patches
+++ b/patches/chromium/.patches
@@ -140,3 +140,4 @@ revert_code_health_clean_up_stale_macwebcontentsocclusion.patch
 ignore_parse_errors_for_pkey_appusermodel_toastactivatorclsid.patch
 feat_add_signals_when_embedder_cleanup_callbacks_run_for.patch
 feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch
+fix_win32_synchronous_spellcheck.patch
diff --git a/patches/chromium/fix_win32_synchronous_spellcheck.patch b/patches/chromium/fix_win32_synchronous_spellcheck.patch
new file mode 100644
index 0000000000000..355c8f8e9a6ae
--- /dev/null
+++ b/patches/chromium/fix_win32_synchronous_spellcheck.patch
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Keeley Hammond <khammond@slack-corp.com>
+Date: Wed, 19 Feb 2025 11:18:44 -0800
+Subject: fix: re-enable synchronous spellcheck on Windows
+
+This fix partially reverts 6109477: Code Health:
+Clean up stale base::Feature "WinRetrieveSuggestionsOnlyOnDemand"
+https://chromium-review.googlesource.com/c/chromium/src/+/6109477
+by re-adding a synchronous call to FillSuggestionList.
+
+This patch can be removed when an asynchronous spellcheck API
+option is added to Electron.
+
+diff --git a/components/spellcheck/browser/windows_spell_checker.cc b/components/spellcheck/browser/windows_spell_checker.cc
+index f5a7411037758427eddc088b5426554b4a500d33..04b3edd4d8c58d38e260cc54beb0dab86368fc25 100644
+--- a/components/spellcheck/browser/windows_spell_checker.cc
++++ b/components/spellcheck/browser/windows_spell_checker.cc
+@@ -239,6 +239,11 @@ std::vector<SpellCheckResult> BackgroundHelper::RequestTextCheckForAllLanguages(
+             (action == CORRECTIVE_ACTION_GET_SUGGESTIONS ||
+              action == CORRECTIVE_ACTION_REPLACE)) {
+           std::vector<std::u16string> suggestions;
++          // TODO (vertedinde): Perform the synchronous operation of retrieving
++          // suggestions for all misspelled words while performing a text check.
++          FillSuggestionList(it->first,
++            text.substr(start_index, error_length),
++            &suggestions);
+           result_map[std::tuple<ULONG, ULONG>(start_index, error_length)]
+               .push_back(suggestions);
+         }
diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc
index f6265505f8803..a18f580f92bba 100644
--- a/shell/browser/api/electron_api_web_contents.cc
+++ b/shell/browser/api/electron_api_web_contents.cc
@@ -194,14 +194,6 @@
 #include "content/public/browser/plugin_service.h"
 #endif
 
-#if BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
-#include "chrome/browser/spellchecker/spellcheck_factory.h"
-#include "chrome/browser/spellchecker/spellcheck_service.h"
-#include "components/spellcheck/browser/spellcheck_platform.h"
-#include "components/spellcheck/common/spellcheck_common.h"
-#include "components/spellcheck/common/spellcheck_features.h"
-#endif
-
 #if !IS_MAS_BUILD()
 #include "chrome/browser/hang_monitor/hang_crash_dump.h"  // nogncheck
 #endif
@@ -1521,44 +1513,11 @@ void WebContents::RendererResponsive(
 
 bool WebContents::HandleContextMenu(content::RenderFrameHost& render_frame_host,
                                     const content::ContextMenuParams& params) {
-#if BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
-  if (!params.misspelled_word.empty() && spellcheck::UseBrowserSpellChecker()) {
-    SpellcheckService* spellcheck_service =
-        SpellcheckServiceFactory::GetForContext(
-            render_frame_host.GetBrowserContext());
-    if (spellcheck_service) {
-      spellcheck_platform::GetPerLanguageSuggestions(
-          spellcheck_service->platform_spell_checker(), params.misspelled_word,
-          base::BindOnce(&WebContents::OnGetPlatformSuggestionsComplete,
-                         GetWeakPtr(), std::ref(render_frame_host), params));
-    }
-  } else {
-#endif
-    Emit("context-menu",
-         std::make_tuple(params, &render_frame_host,
-                         std::optional<std::vector<std::u16string>>{}));
-#if BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
-  }
-#endif
+  Emit("context-menu", std::make_pair(params, &render_frame_host));
 
   return true;
 }
 
-#if BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
-void WebContents::OnGetPlatformSuggestionsComplete(
-    content::RenderFrameHost& render_frame_host,
-    const content::ContextMenuParams& params,
-    const spellcheck::PerLanguageSuggestions&
-        platform_per_language_suggestions) {
-  std::vector<std::u16string> combined_suggestions;
-  spellcheck::FillSuggestions(platform_per_language_suggestions,
-                              &combined_suggestions);
-  Emit("context-menu",
-       std::make_tuple(params, &render_frame_host,
-                       std::make_optional(combined_suggestions)));
-}
-#endif
-
 void WebContents::FindReply(content::WebContents* web_contents,
                             int request_id,
                             int number_of_matches,
diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h
index 58a6de68d24e5..40a0502b66924 100644
--- a/shell/browser/api/electron_api_web_contents.h
+++ b/shell/browser/api/electron_api_web_contents.h
@@ -57,10 +57,6 @@ class ScriptExecutor;
 }
 #endif
 
-#if BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
-#include "components/spellcheck/common/spellcheck_common.h"
-#endif
-
 namespace blink {
 struct DeviceEmulationParams;
 // enum class PermissionType;
@@ -761,14 +757,6 @@ class WebContents final : public ExclusiveAccessContext,
   // Update the html fullscreen flag in both browser and renderer.
   void UpdateHtmlApiFullscreen(bool fullscreen);
 
-#if BUILDFLAG(IS_WIN) && BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
-  void OnGetPlatformSuggestionsComplete(
-      content::RenderFrameHost& render_frame_host,
-      const content::ContextMenuParams& params,
-      const spellcheck::PerLanguageSuggestions&
-          platform_per_language_suggestions);
-#endif
-
   v8::Global<v8::Value> session_;
   v8::Global<v8::Value> devtools_web_contents_;
   v8::Global<v8::Value> debugger_;
diff --git a/shell/common/gin_converters/content_converter.cc b/shell/common/gin_converters/content_converter.cc
index 7e593c324a0dd..d5a257b8f24ac 100644
--- a/shell/common/gin_converters/content_converter.cc
+++ b/shell/common/gin_converters/content_converter.cc
@@ -88,7 +88,8 @@ v8::Local<v8::Value> Converter<blink::mojom::MenuItem::Type>::ToV8(
 v8::Local<v8::Value> Converter<ContextMenuParamsWithRenderFrameHost>::ToV8(
     v8::Isolate* isolate,
     const ContextMenuParamsWithRenderFrameHost& val) {
-  auto [params, render_frame_host, optional_suggestions] = val;
+  const auto& params = val.first;
+  content::RenderFrameHost* render_frame_host = val.second;
   auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
   dict.SetGetter("frame", render_frame_host, v8::DontEnum);
   dict.Set("x", params.x);
@@ -113,11 +114,7 @@ v8::Local<v8::Value> Converter<ContextMenuParamsWithRenderFrameHost>::ToV8(
   dict.Set("misspelledWord", params.misspelled_word);
   dict.Set("selectionRect", params.selection_rect);
 #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
-  if (optional_suggestions) {
-    dict.Set("dictionarySuggestions", optional_suggestions.value());
-  } else {
-    dict.Set("dictionarySuggestions", params.dictionary_suggestions);
-  }
+  dict.Set("dictionarySuggestions", params.dictionary_suggestions);
   dict.Set("spellcheckEnabled", params.spellcheck_enabled);
 #else
   dict.Set("spellcheckEnabled", false);
diff --git a/shell/common/gin_converters/content_converter.h b/shell/common/gin_converters/content_converter.h
index 1e1f26012f874..2fa5f7f118df9 100644
--- a/shell/common/gin_converters/content_converter.h
+++ b/shell/common/gin_converters/content_converter.h
@@ -26,9 +26,7 @@ struct NativeWebKeyboardEvent;
 }
 
 using ContextMenuParamsWithRenderFrameHost =
-    std::tuple<content::ContextMenuParams,
-               content::RenderFrameHost*,
-               std::optional<std::vector<std::u16string>>>;
+    std::pair<content::ContextMenuParams, content::RenderFrameHost*>;
 
 namespace gin {
 


Test patch: