From 6bcf67e051ecd3352d8f1db338926afa77e8bc90 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Thu, 31 Oct 2019 13:11:51 -0700 Subject: [PATCH] feat: enable builtin spellchecker (#20692) * chore: add code required to use chromes spellchecker * chore: fix linting * chore: manifests needs buildflags now * chore: add dictionarySuggestions to the context menu event when the spellchecker is active * chore: enable by default for windows builds * chore: add patch to remove incognito usage in the spellchecker * chore: add dependencies on spellcheck common and flags * chore: conditionally include spell check panel impl * chore: fix deps for spellcheck feature flags * chore: add patch for electron resources * chore: add dependency on //components/language/core/browser * chore: patches to make hunspell work on windows * build: collect hunspell dictionaries into a zip file and publish * chore: clean up patches * chore: add docs and set spell checker url method * chore: fix error handling * chore: fix hash logic * build: update hunspell filename generator * fix: default spellchecker list to the current system locale if we can * docs: document the language getter * chore: patch IDS_ resources for linux builds * feat: add spellcheck webpref flag to disable the builtin spellchecker * chore: fix docs typo * chore: clean up spellchecker impl as per feedback * remove unneeded deps --- .circleci/config.yml | 29 ++++++++- BUILD.gn | 31 +++++++++- appveyor.yml | 2 + build/zip.py | 7 ++- buildflags/BUILD.gn | 1 + buildflags/buildflags.gni | 3 + chromium_src/BUILD.gn | 50 ++++++++++++++++ docs/api/browser-window.md | 2 + docs/api/session.md | 28 +++++++++ docs/api/web-contents.md | 3 + docs/api/web-frame.md | 11 ++++ electron_strings.grdp | 6 ++ filenames.hunspell.gni | 60 +++++++++++++++++++ package.json | 3 + patches/chromium/.patches | 2 + ...esources_not_chrome_for_spellchecker.patch | 34 +++++++++++ ...f_incognito_apis_in_the_spellchecker.patch | 25 ++++++++ script/gen-hunspell-filenames.js | 35 +++++++++++ script/generate-deps-hash.js | 1 + script/release/release.js | 1 + script/release/uploaders/upload.py | 5 ++ shell/browser/api/atom_api_app.cc | 7 ++- shell/browser/api/atom_api_session.cc | 50 ++++++++++++++++ shell/browser/api/atom_api_session.h | 5 ++ shell/browser/atom_browser_client.cc | 17 ++++++ shell/browser/atom_browser_client.h | 5 ++ shell/browser/atom_browser_context.cc | 44 +++++++++++++- shell/browser/atom_browser_main_parts.cc | 8 +++ shell/browser/web_contents_preferences.cc | 9 +++ .../gin_converters/content_converter.cc | 3 + shell/common/options_switches.cc | 8 +++ shell/common/options_switches.h | 8 +++ shell/renderer/renderer_client_base.cc | 40 +++++++++++++ shell/renderer/renderer_client_base.h | 27 ++++++++- 34 files changed, 560 insertions(+), 10 deletions(-) create mode 100644 filenames.hunspell.gni create mode 100644 patches/chromium/chore_use_electron_resources_not_chrome_for_spellchecker.patch create mode 100644 patches/chromium/remove_usage_of_incognito_apis_in_the_spellchecker.patch create mode 100644 script/gen-hunspell-filenames.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 17d6083b3a..51c9117308 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -319,6 +319,8 @@ step-gn-check: &step-gn-check gn check out/Default //electron:electron_app gn check out/Default //electron:manifests gn check out/Default //electron/shell/common/api:mojo + # Check the hunspell filenames + node electron/script/gen-hunspell-filenames.js --check step-electron-build: &step-electron-build run: @@ -534,6 +536,20 @@ step-mksnapshot-store: &step-mksnapshot-store path: src/out/Default/mksnapshot.zip destination: mksnapshot.zip +step-hunspell-build: &step-hunspell-build + run: + name: hunspell build + command: | + cd src + if [ "$SKIP_DIST_ZIP" != "1" ]; then + ninja -C out/Default electron:hunspell_dictionaries_zip -j $NUMBER_OF_NINJA_PROCESSES + fi + +step-hunspell-store: &step-hunspell-store + store_artifacts: + path: src/out/Default/hunspell_dictionaries.zip + destination: hunspell_dictionaries.zip + step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols run: name: Generate breakpad symbols @@ -693,7 +709,6 @@ step-minimize-workspace-size-from-checkout: &step-minimize-workspace-size-from-c rm -rf src/ios rm -rf src/third_party/blink/web_tests rm -rf src/third_party/blink/perf_tests - rm -rf src/third_party/hunspell_dictionaries rm -rf src/third_party/WebKit/LayoutTests # Save the src cache based on the deps hash @@ -912,6 +927,10 @@ steps-electron-build: &steps-electron-build - *step-ffmpeg-build - *step-ffmpeg-store + # hunspell + - *step-hunspell-build + - *step-hunspell-store + # Save all data needed for a further tests run. - *step-persist-data-for-tests @@ -990,6 +1009,10 @@ steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with- - *step-ffmpeg-build - *step-ffmpeg-store + # hunspell + - *step-hunspell-build + - *step-hunspell-store + # Save all data needed for a further tests run. - *step-persist-data-for-tests @@ -1079,6 +1102,10 @@ steps-electron-build-for-publish: &steps-electron-build-for-publish - *step-ffmpeg-build - *step-ffmpeg-store + # hunspell + - *step-hunspell-build + - *step-hunspell-store + # typescript defs - *step-maybe-generate-typescript-defs diff --git a/BUILD.gn b/BUILD.gn index c6a25e0756..2bb0b07685 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,6 +1,7 @@ import("//build/config/locales.gni") import("//build/config/ui.gni") import("//build/config/win/manifest.gni") +import("//components/spellcheck/spellcheck_build_features.gni") import("//content/public/app/mac_helpers.gni") import("//pdf/features.gni") import("//printing/buildflags/buildflags.gni") @@ -21,6 +22,7 @@ import("buildflags/buildflags.gni") import("electron_paks.gni") import("filenames.auto.gni") import("filenames.gni") +import("filenames.hunspell.gni") if (is_mac) { import("//build/config/mac/rules.gni") @@ -358,12 +360,12 @@ source_set("electron_lib") { "//chrome/app/resources:platform_locale_settings", "//chrome/services/printing/public/mojom", "//components/certificate_transparency", + "//components/language/core/browser", "//components/net_log", "//components/network_hints/common", "//components/network_hints/renderer", "//components/network_session_configurator/common", "//components/prefs", - "//components/spellcheck/renderer", "//components/viz/host", "//components/viz/service", "//content/public/browser", @@ -478,6 +480,10 @@ source_set("electron_lib") { ] } + if (enable_builtin_spellchecker) { + deps += [ "chromium_src:chrome_spellchecker" ] + } + if (is_mac) { deps += [ "//components/remote_cocoa/app_shim", @@ -1268,9 +1274,14 @@ template("dist_zip") { "outputs", "testonly", ]) + flatten = false + if (defined(invoker.flatten)) { + flatten = invoker.flatten + } args = rebase_path(outputs + [ _runtime_deps_file ], root_build_dir) + [ target_cpu, target_os, + "$flatten", ] } } @@ -1356,6 +1367,24 @@ dist_zip("electron_mksnapshot_zip") { ] } +copy("hunspell_dictionaries") { + sources = hunspell_dictionaries + hunspell_licenses + outputs = [ + "$target_gen_dir/electron_hunspell/{{source_file_part}}", + ] +} + +dist_zip("hunspell_dictionaries_zip") { + data_deps = [ + ":hunspell_dictionaries", + ] + flatten = true + + outputs = [ + "$root_build_dir/hunspell_dictionaries.zip", + ] +} + group("electron") { public_deps = [ ":electron_app", diff --git a/appveyor.yml b/appveyor.yml index 618a25345e..26e29919b8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -91,6 +91,7 @@ build_script: - ninja -C out/Default electron:electron_dist_zip - ninja -C out/Default shell_browser_ui_unittests - ninja -C out/Default electron:electron_mksnapshot_zip + - ninja -C out/Default electron:hunspell_dictionaries_zip - ninja -C out/Default electron:electron_chromedriver_zip - ninja -C out/Default third_party/electron_node:headers - appveyor PushArtifact out/Default/dist.zip @@ -100,6 +101,7 @@ build_script: - 7z a node_headers.zip out\Default\gen\node_headers - appveyor PushArtifact node_headers.zip - appveyor PushArtifact out/Default/mksnapshot.zip + - appveyor PushArtifact out/Default/hunspell_dictionaries.zip - appveyor PushArtifact out/Default/electron.lib - ps: >- if ($env:GN_CONFIG -eq 'release') { diff --git a/build/zip.py b/build/zip.py index ff9c888d91..00a6bc9de0 100644 --- a/build/zip.py +++ b/build/zip.py @@ -46,13 +46,14 @@ def execute(argv): raise e def main(argv): - dist_zip, runtime_deps, target_cpu, target_os = argv + dist_zip, runtime_deps, target_cpu, target_os, flatten_val = argv + should_flatten = flatten_val == "true" dist_files = set() with open(runtime_deps) as f: for dep in f.readlines(): dep = dep.strip() dist_files.add(dep) - if sys.platform == 'darwin': + if sys.platform == 'darwin' and not should_flatten: execute(['zip', '-r', '-y', dist_zip] + list(dist_files)) else: with zipfile.ZipFile(dist_zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as z: @@ -67,7 +68,7 @@ def main(argv): basename = os.path.basename(dep) dirname = os.path.dirname(dep) arcname = os.path.join(dirname, 'chrome-sandbox') if basename == 'chrome_sandbox' else dep - z.write(dep, arcname) + z.write(dep, os.path.basename(arcname) if should_flatten else arcname) if __name__ == '__main__': sys.exit(main(sys.argv[1:])) diff --git a/buildflags/BUILD.gn b/buildflags/BUILD.gn index 2da174f3e9..6209a7de40 100644 --- a/buildflags/BUILD.gn +++ b/buildflags/BUILD.gn @@ -19,6 +19,7 @@ buildflag_header("buildflags") { "ENABLE_TTS=$enable_tts", "ENABLE_COLOR_CHOOSER=$enable_color_chooser", "ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions", + "ENABLE_BUILTIN_SPELLCHECKER=$enable_builtin_spellchecker", "ENABLE_PICTURE_IN_PICTURE=$enable_picture_in_picture", "OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider", ] diff --git a/buildflags/buildflags.gni b/buildflags/buildflags.gni index a4d4a48714..9bd6fe1ed6 100644 --- a/buildflags/buildflags.gni +++ b/buildflags/buildflags.gni @@ -33,4 +33,7 @@ declare_args() { # Enable Chrome extensions support. enable_electron_extensions = false + + # Enable Spellchecker support + enable_builtin_spellchecker = true } diff --git a/chromium_src/BUILD.gn b/chromium_src/BUILD.gn index e8313cd8df..d6796f796c 100644 --- a/chromium_src/BUILD.gn +++ b/chromium_src/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/config/ui.gni") +import("//components/spellcheck/spellcheck_build_features.gni") import("//electron/buildflags/buildflags.gni") import("//printing/buildflags/buildflags.gni") import("//third_party/widevine/cdm/widevine.gni") @@ -225,3 +226,52 @@ static_library("chrome") { ] } } + +# This source set is just so we don't have to depend on all of //chrome/browser +# You may have to add new files here during the upgrade if //chrome/browser/spellchecker +# gets more files +source_set("chrome_spellchecker") { + sources = [ + "//chrome/browser/spellchecker/spell_check_host_chrome_impl.cc", + "//chrome/browser/spellchecker/spell_check_host_chrome_impl.h", + "//chrome/browser/spellchecker/spellcheck_custom_dictionary.cc", + "//chrome/browser/spellchecker/spellcheck_custom_dictionary.h", + "//chrome/browser/spellchecker/spellcheck_factory.cc", + "//chrome/browser/spellchecker/spellcheck_factory.h", + "//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc", + "//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h", + "//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc", + "//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.h", + "//chrome/browser/spellchecker/spellcheck_language_policy_handler.cc", + "//chrome/browser/spellchecker/spellcheck_language_policy_handler.h", + "//chrome/browser/spellchecker/spellcheck_service.cc", + "//chrome/browser/spellchecker/spellcheck_service.h", + ] + + if (has_spellcheck_panel) { + sources += [ + "//chrome/browser/spellchecker/spell_check_panel_host_impl.cc", + "//chrome/browser/spellchecker/spell_check_panel_host_impl.h", + ] + } + + if (use_browser_spellchecker) { + sources += [ + "//chrome/browser/spellchecker/spelling_request.cc", + "//chrome/browser/spellchecker/spelling_request.h", + ] + } + + deps = [ + "//base:base_static", + "//components/language/core/browser", + "//components/spellcheck:buildflags", + "//components/sync", + ] + + public_deps = [ + "//components/spellcheck/browser", + "//components/spellcheck/common", + "//components/spellcheck/renderer", + ] +} diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 928eff7600..f74b25d089 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -385,6 +385,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. * `accessibleTitle` String (optional) - An alternative title string provided only to accessibility tools such as screen readers. This string is not directly visible to users. + * `spellcheck` Boolean (optional) - Whether to enable the builtin spellchecker. + Default is `true`. When setting minimum or maximum window size with `minWidth`/`maxWidth`/ `minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from diff --git a/docs/api/session.md b/docs/api/session.md index 7eec76e226..27c6d96091 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -456,10 +456,38 @@ this session just before normal `preload` scripts run. Returns `String[]` an array of paths to preload scripts that have been registered. +#### `ses.setSpellCheckerLanguages(languages)` + +* `languages` String[] - An array of language codes to enable the spellchecker for. + +The built in spellchecker does not automatically detect what language a user is typing in. In order for the +spell checker to correctly check their words you must call this API with an array of language codes. You can +get the list of supported language codes with the `ses.availableSpellCheckerLanguages` property. + +#### `ses.getSpellCheckerLanguages()` + +Returns `String[]` - An array of language codes the spellchecker is enabled for. If this list is empty the spellchecker +will fallback to using `en-US`. By default on launch if this setting is an empty list Electron will try to populate this +setting with the current OS locale. This setting is persisted across restarts. + +#### `ses.setSpellCheckerDictionaryDownloadURL(url)` + +* `url` String - A base URL for Electron to download hunspell dictionaries from. + +By default Electron will download hunspell dictionaries from the Chromium CDN. If you want to override this +behavior you can use this API to point the dictionary downloader at your own hosted version of the hunspell +dictionaries. We publish a `hunspell_dictionaries.zip` file with each release which contains the files you need +to host here. + ### Instance Properties The following properties are available on instances of `Session`: +#### `ses.availableSpellCheckerLanguages` _Readonly_ + +A `String[]` array which consists of all the known available spell checker languages. Providing a language +code to the `setSpellCheckerLanaguages` API that isn't in this array will result in an error. + #### `ses.cookies` _Readonly_ A [`Cookies`](cookies.md) object for this session. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index ec335b061f..69758f61d9 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -570,6 +570,9 @@ Returns: * `titleText` String - Title or alt text of the selection that the context was invoked on. * `misspelledWord` String - The misspelled word under the cursor, if any. + * `dictionarySuggestions` String[] - An array of suggested words to show the + user to replace the `misspelledWord`. Only available if there is a misspelled + word and spellchecker is enabled. * `frameCharset` String - The character encoding of the frame on which the menu was invoked. * `inputFieldType` String - If the context menu was invoked on an input diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index 712aca4396..f39dc70a06 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -74,6 +74,17 @@ Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. Sets a provider for spell checking in input fields and text areas. +If you want to use this method you must disable the builtin spellchecker when you +construct the window. + +```js +const mainWindow = new BrowserWindow({ + webPreferences: { + spellcheck: false + } +}) +``` + The `provider` must be an object that has a `spellCheck` method that accepts an array of individual words for spellchecking. The `spellCheck` function runs asynchronously and calls the `callback` function diff --git a/electron_strings.grdp b/electron_strings.grdp index ca59124720..5b3f39afcf 100644 --- a/electron_strings.grdp +++ b/electron_strings.grdp @@ -69,4 +69,10 @@ Previous track + + en-US + + + en-US,en + diff --git a/filenames.hunspell.gni b/filenames.hunspell.gni new file mode 100644 index 0000000000..8662408760 --- /dev/null +++ b/filenames.hunspell.gni @@ -0,0 +1,60 @@ +hunspell_dictionaries = [ + "//third_party/hunspell_dictionaries/af-ZA-3-0.bdic", + "//third_party/hunspell_dictionaries/bg-BG-3-0.bdic", + "//third_party/hunspell_dictionaries/ca-ES-3-0.bdic", + "//third_party/hunspell_dictionaries/cs-CZ-3-0.bdic", + "//third_party/hunspell_dictionaries/cy-GB-1-0.bdic", + "//third_party/hunspell_dictionaries/da-DK-3-0.bdic", + "//third_party/hunspell_dictionaries/de-DE-3-0.bdic", + "//third_party/hunspell_dictionaries/el-GR-3-0.bdic", + "//third_party/hunspell_dictionaries/en-AU-8-0.bdic", + "//third_party/hunspell_dictionaries/en-CA-8-0.bdic", + "//third_party/hunspell_dictionaries/en-GB-8-0.bdic", + "//third_party/hunspell_dictionaries/en-US-8-0.bdic", + "//third_party/hunspell_dictionaries/es-ES-3-0.bdic", + "//third_party/hunspell_dictionaries/et-EE-3-0.bdic", + "//third_party/hunspell_dictionaries/fa-IR-8-0.bdic", + "//third_party/hunspell_dictionaries/fo-FO-3-0.bdic", + "//third_party/hunspell_dictionaries/fr-FR-3-0.bdic", + "//third_party/hunspell_dictionaries/he-IL-3-0.bdic", + "//third_party/hunspell_dictionaries/hi-IN-3-0.bdic", + "//third_party/hunspell_dictionaries/hr-HR-3-0.bdic", + "//third_party/hunspell_dictionaries/hu-HU-3-0.bdic", + "//third_party/hunspell_dictionaries/hy-1-0.bdic", + "//third_party/hunspell_dictionaries/id-ID-3-0.bdic", + "//third_party/hunspell_dictionaries/it-IT-3-0.bdic", + "//third_party/hunspell_dictionaries/ko-3-0.bdic", + "//third_party/hunspell_dictionaries/lt-LT-3-0.bdic", + "//third_party/hunspell_dictionaries/lv-LV-3-0.bdic", + "//third_party/hunspell_dictionaries/nb-NO-3-0.bdic", + "//third_party/hunspell_dictionaries/nl-NL-3-0.bdic", + "//third_party/hunspell_dictionaries/pl-PL-3-0.bdic", + "//third_party/hunspell_dictionaries/pt-BR-3-0.bdic", + "//third_party/hunspell_dictionaries/pt-PT-3-0.bdic", + "//third_party/hunspell_dictionaries/ro-RO-3-0.bdic", + "//third_party/hunspell_dictionaries/ru-RU-3-0.bdic", + "//third_party/hunspell_dictionaries/sh-3-0.bdic", + "//third_party/hunspell_dictionaries/sh-4-0.bdic", + "//third_party/hunspell_dictionaries/sk-SK-3-0.bdic", + "//third_party/hunspell_dictionaries/sl-SI-3-0.bdic", + "//third_party/hunspell_dictionaries/sq-3-0.bdic", + "//third_party/hunspell_dictionaries/sr-3-0.bdic", + "//third_party/hunspell_dictionaries/sr-4-0.bdic", + "//third_party/hunspell_dictionaries/sv-SE-3-0.bdic", + "//third_party/hunspell_dictionaries/ta-IN-3-0.bdic", + "//third_party/hunspell_dictionaries/tg-TG-5-0.bdic", + "//third_party/hunspell_dictionaries/tr-TR-4-0.bdic", + "//third_party/hunspell_dictionaries/uk-UA-3-0.bdic", + "//third_party/hunspell_dictionaries/vi-VN-3-0.bdic", + "//third_party/hunspell_dictionaries/xx-XX-3-0.bdic", +] + +hunspell_licenses = [ + "//third_party/hunspell_dictionaries/COPYING", + "//third_party/hunspell_dictionaries/COPYING.Apache", + "//third_party/hunspell_dictionaries/COPYING.LESSER", + "//third_party/hunspell_dictionaries/COPYING.LGPL", + "//third_party/hunspell_dictionaries/COPYING.MIT", + "//third_party/hunspell_dictionaries/COPYING.MPL", + "//third_party/hunspell_dictionaries/LICENSE", +] diff --git a/package.json b/package.json index 9f154f15bd..b17c2e4d69 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,9 @@ "node script/gen-filenames.js", "python script/check-trailing-whitespace.py --fix", "git add filenames.auto.gni" + ], + "DEPS": [ + "node script/gen-hunspell-filenames.js" ] }, "dependencies": { diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 666df63142..afd3a38643 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -81,3 +81,5 @@ revert_remove_contentrendererclient_shouldfork.patch build_win_disable_zc_twophase.patch ignore_rc_check.patch build_win_fix_ambiguous_reference_with_msstl.patch +remove_usage_of_incognito_apis_in_the_spellchecker.patch +chore_use_electron_resources_not_chrome_for_spellchecker.patch diff --git a/patches/chromium/chore_use_electron_resources_not_chrome_for_spellchecker.patch b/patches/chromium/chore_use_electron_resources_not_chrome_for_spellchecker.patch new file mode 100644 index 0000000000..7a3ab1cbdb --- /dev/null +++ b/patches/chromium/chore_use_electron_resources_not_chrome_for_spellchecker.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Wed, 23 Oct 2019 14:17:18 -0700 +Subject: chore: use electron resources not chrome for spellchecker + +spellchecker uses a few IDS_ resources. We need to load these from +Electrons grit header instead of Chromes + +diff --git a/chrome/browser/spellchecker/spellcheck_factory.cc b/chrome/browser/spellchecker/spellcheck_factory.cc +index 48ac0a24efde0cb7d3ba71c8b8bdf5178f606e80..e2beefc276098fdc8f1cdab2e0edb8fae4ee67ca 100644 +--- a/chrome/browser/spellchecker/spellcheck_factory.cc ++++ b/chrome/browser/spellchecker/spellcheck_factory.cc +@@ -6,7 +6,7 @@ + + #include "chrome/browser/profiles/incognito_helpers.h" + #include "chrome/browser/spellchecker/spellcheck_service.h" +-#include "chrome/grit/locale_settings.h" ++#include "electron/grit/electron_resources.h" + #include "components/keyed_service/content/browser_context_dependency_manager.h" + #include "components/pref_registry/pref_registry_syncable.h" + #include "components/prefs/pref_service.h" +diff --git a/components/language/core/browser/language_prefs.cc b/components/language/core/browser/language_prefs.cc +index d5e4c09e1722232df44b112ce39cdacea03a4710..c6caf7eacd9eed439ab5167e51b9fcce5d6af664 100644 +--- a/components/language/core/browser/language_prefs.cc ++++ b/components/language/core/browser/language_prefs.cc +@@ -21,7 +21,7 @@ + #include "components/pref_registry/pref_registry_syncable.h" + #include "components/prefs/pref_service.h" + #include "components/prefs/scoped_user_pref_update.h" +-#include "components/strings/grit/components_locale_settings.h" ++#include "electron/grit/electron_resources.h" + #include "ui/base/l10n/l10n_util.h" + + namespace language { diff --git a/patches/chromium/remove_usage_of_incognito_apis_in_the_spellchecker.patch b/patches/chromium/remove_usage_of_incognito_apis_in_the_spellchecker.patch new file mode 100644 index 0000000000..80e1100f31 --- /dev/null +++ b/patches/chromium/remove_usage_of_incognito_apis_in_the_spellchecker.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Wed, 23 Oct 2019 11:43:58 -0700 +Subject: remove usage of incognito APIs in the spellchecker + +chrome::GetBrowserContextRedirectedInIncognito is not available in +Electron nor do we want it to be. We could potentially upstream a +change to move more of //chrome spellchecker logic into //components so +that we can further separate our dependency from //chrome. + +diff --git a/chrome/browser/spellchecker/spellcheck_factory.cc b/chrome/browser/spellchecker/spellcheck_factory.cc +index 5253d1f48e188b0339834c876378677b459e718e..48ac0a24efde0cb7d3ba71c8b8bdf5178f606e80 100644 +--- a/chrome/browser/spellchecker/spellcheck_factory.cc ++++ b/chrome/browser/spellchecker/spellcheck_factory.cc +@@ -78,7 +78,10 @@ void SpellcheckServiceFactory::RegisterProfilePrefs( + + content::BrowserContext* SpellcheckServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { ++ return context; ++#if 0 + return chrome::GetBrowserContextRedirectedInIncognito(context); ++#endif + } + + bool SpellcheckServiceFactory::ServiceIsNULLWhileTesting() const { diff --git a/script/gen-hunspell-filenames.js b/script/gen-hunspell-filenames.js new file mode 100644 index 0000000000..e91f935018 --- /dev/null +++ b/script/gen-hunspell-filenames.js @@ -0,0 +1,35 @@ +const fs = require('fs') +const path = require('path') + +const check = process.argv.includes('--check') + +const dictsPath = path.resolve(__dirname, '..', '..', 'third_party', 'hunspell_dictionaries') +const gclientPath = 'third_party/hunspell_dictionaries' + +const allFiles = fs.readdirSync(dictsPath) + +const dictionaries = allFiles + .filter(file => path.extname(file) === '.bdic') + +const licenses = allFiles + .filter(file => file.startsWith('LICENSE') || file.startsWith('COPYING')) + +const content = `hunspell_dictionaries = [ + ${dictionaries.map(f => `"//${path.posix.join(gclientPath, f)}"`).join(',\n ')}, +] + +hunspell_licenses = [ + ${licenses.map(f => `"//${path.posix.join(gclientPath, f)}"`).join(',\n ')}, +] +` + +const filenamesPath = path.resolve(__dirname, '..', 'filenames.hunspell.gni') + +if (check) { + const currentContent = fs.readFileSync(filenamesPath, 'utf8') + if (currentContent !== content) { + throw new Error('hunspell filenames need to be regenerated, latest generation does not match current file. Please run node gen-hunspell-filenames.js') + } +} else { + fs.writeFileSync(filenamesPath, content) +} diff --git a/script/generate-deps-hash.js b/script/generate-deps-hash.js index df5dc2a9f0..7cd5a84f3e 100644 --- a/script/generate-deps-hash.js +++ b/script/generate-deps-hash.js @@ -29,6 +29,7 @@ addAllFiles(path.resolve(__dirname, '../patches')) // Create Hash const hasher = crypto.createHash('SHA256') +hasher.update(`HASH_VERSION:${HASH_VERSION}`) for (const file of filesToHash) { hasher.update(fs.readFileSync(file)) } diff --git a/script/release/release.js b/script/release/release.js index 43f0e647ca..f9e102ab96 100755 --- a/script/release/release.js +++ b/script/release/release.js @@ -130,6 +130,7 @@ function assetsForVersion (version, validatingRelease) { `electron-${version}-win32-arm64.zip`, `electron-api.json`, `electron.d.ts`, + `hunspell_dictionaries.zip`, `ffmpeg-${version}-darwin-x64.zip`, `ffmpeg-${version}-linux-arm64.zip`, `ffmpeg-${version}-linux-armv7l.zip`, diff --git a/script/release/uploaders/upload.py b/script/release/uploaders/upload.py index de0954a914..15f303db5f 100755 --- a/script/release/uploaders/upload.py +++ b/script/release/uploaders/upload.py @@ -106,6 +106,11 @@ def main(): shutil.copy2(os.path.join(OUT_DIR, 'mksnapshot.zip'), mksnapshot_zip) upload_electron(release, mksnapshot_zip, args) + if PLATFORM == 'linux' and get_target_arch() == 'x64': + # Upload the hunspell dictionaries only from the linux x64 build + hunspell_dictionaries_zip = os.path.join(OUT_DIR, 'hunspell_dictionaries.zip') + upload_electron(release, hunspell_dictionaries_zip, args) + if not tag_exists and not args.upload_to_s3: # Upload symbols to symbol server. run_python_upload_script('upload-symbols.py') diff --git a/shell/browser/api/atom_api_app.cc b/shell/browser/api/atom_api_app.cc index 4dd6bd8c44..3f1d786bf7 100644 --- a/shell/browser/api/atom_api_app.cc +++ b/shell/browser/api/atom_api_app.cc @@ -898,9 +898,14 @@ void App::SetPath(gin_helper::ErrorThrower thrower, bool succeed = false; int key = GetPathConstant(name); - if (key >= 0) + if (key >= 0) { succeed = base::PathService::OverrideAndCreateIfNeeded(key, path, true, false); + if (key == DIR_USER_DATA) { + succeed |= base::PathService::OverrideAndCreateIfNeeded( + chrome::DIR_USER_DATA, path, true, false); + } + } if (!succeed) thrower.ThrowError("Failed to set path"); } diff --git a/shell/browser/api/atom_api_session.cc b/shell/browser/api/atom_api_session.cc index 424a8142de..9300c6b9d1 100644 --- a/shell/browser/api/atom_api_session.cc +++ b/shell/browser/api/atom_api_session.cc @@ -68,6 +68,12 @@ #include "shell/browser/extensions/atom_extension_system.h" #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h" +#include "components/spellcheck/browser/pref_names.h" +#include "components/spellcheck/common/spellcheck_common.h" +#endif + using content::BrowserThread; using content::StoragePartition; @@ -646,6 +652,42 @@ void Session::Preconnect(const gin_helper::Dictionary& options, url, num_sockets_to_preconnect)); } +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +base::Value Session::GetSpellCheckerLanguages() { + return browser_context_->prefs() + ->Get(spellcheck::prefs::kSpellCheckDictionaries) + ->Clone(); +} + +void Session::SetSpellCheckerLanguages( + gin_helper::ErrorThrower thrower, + const std::vector& languages) { + base::ListValue language_codes; + for (const std::string& lang : languages) { + std::string code = spellcheck::GetCorrespondingSpellCheckLanguage(lang); + if (code.empty()) { + thrower.ThrowError("Invalid language code provided: \"" + lang + + "\" is not a valid language code"); + return; + } + language_codes.AppendString(code); + } + browser_context_->prefs()->Set(spellcheck::prefs::kSpellCheckDictionaries, + language_codes); +} + +void SetSpellCheckerDictionaryDownloadURL(gin_helper::ErrorThrower thrower, + const GURL& url) { + if (!url.is_valid()) { + thrower.ThrowError( + "The URL you provided to setSpellCheckerDictionaryDownloadURL is not a " + "valid URL"); + return; + } + SpellcheckHunspellDictionary::SetDownloadURLForTesting(url); +} +#endif + // static gin::Handle Session::CreateFrom(v8::Isolate* isolate, AtomBrowserContext* browser_context) { @@ -716,6 +758,14 @@ void Session::BuildPrototype(v8::Isolate* isolate, .SetMethod("getPreloads", &Session::GetPreloads) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) .SetMethod("loadChromeExtension", &Session::LoadChromeExtension) +#endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + .SetMethod("getSpellCheckerLanguages", &Session::GetSpellCheckerLanguages) + .SetMethod("setSpellCheckerLanguages", &Session::SetSpellCheckerLanguages) + .SetProperty("availableSpellCheckerLanguages", + &spellcheck::SpellCheckLanguages) + .SetMethod("setSpellCheckerDictionaryDownloadURL", + &SetSpellCheckerDictionaryDownloadURL) #endif .SetMethod("preconnect", &Session::Preconnect) .SetProperty("cookies", &Session::Cookies) diff --git a/shell/browser/api/atom_api_session.h b/shell/browser/api/atom_api_session.h index 8bd766b8bb..5ac404af3e 100644 --- a/shell/browser/api/atom_api_session.h +++ b/shell/browser/api/atom_api_session.h @@ -88,6 +88,11 @@ class Session : public gin_helper::TrackableObject, v8::Local NetLog(v8::Isolate* isolate); void Preconnect(const gin_helper::Dictionary& options, gin_helper::Arguments* args); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + base::Value GetSpellCheckerLanguages(); + void SetSpellCheckerLanguages(gin_helper::ErrorThrower thrower, + const std::vector& languages); +#endif #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) void LoadChromeExtension(const base::FilePath extension_path); diff --git a/shell/browser/atom_browser_client.cc b/shell/browser/atom_browser_client.cc index edb592c867..5c2a6fecc9 100644 --- a/shell/browser/atom_browser_client.cc +++ b/shell/browser/atom_browser_client.cc @@ -100,6 +100,11 @@ #include "net/ssl/client_cert_store.h" #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h" // nogncheck +#include "components/spellcheck/common/spellcheck.mojom.h" // nogncheck +#endif + #if BUILDFLAG(ENABLE_PEPPER_FLASH) #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h" #endif // BUILDFLAG(ENABLE_PEPPER_FLASH) @@ -758,6 +763,18 @@ network::mojom::NetworkContext* AtomBrowserClient::GetSystemNetworkContext() { return g_browser_process->system_network_context_manager()->GetContext(); } +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +void AtomBrowserClient::BindHostReceiverForRenderer( + content::RenderProcessHost* render_process_host, + mojo::GenericPendingReceiver receiver) { + if (auto host_receiver = receiver.As()) { + SpellCheckHostChromeImpl::Create(render_process_host->GetID(), + std::move(host_receiver)); + return; + } +} +#endif + base::Optional AtomBrowserClient::GetServiceManifestOverlay(base::StringPiece name) { if (name == content::mojom::kBrowserServiceName) diff --git a/shell/browser/atom_browser_client.h b/shell/browser/atom_browser_client.h index b4631b5620..7ce978d249 100644 --- a/shell/browser/atom_browser_client.h +++ b/shell/browser/atom_browser_client.h @@ -149,6 +149,11 @@ class AtomBrowserClient : public content::ContentBrowserClient, bool in_memory, const base::FilePath& relative_partition_path) override; network::mojom::NetworkContext* GetSystemNetworkContext() override; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + void BindHostReceiverForRenderer( + content::RenderProcessHost* render_process_host, + mojo::GenericPendingReceiver receiver) override; +#endif base::Optional GetServiceManifestOverlay( base::StringPiece name) override; content::MediaObserver* GetMediaObserver() override; diff --git a/shell/browser/atom_browser_context.cc b/shell/browser/atom_browser_context.cc index 9175639509..aa83f66ed5 100644 --- a/shell/browser/atom_browser_context.cc +++ b/shell/browser/atom_browser_context.cc @@ -48,8 +48,6 @@ #include "shell/common/options_switches.h" #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) -#include "components/pref_registry/pref_registry_syncable.h" -#include "components/user_prefs/user_prefs.h" #include "extensions/browser/browser_context_keyed_service_factories.h" #include "extensions/browser/extension_pref_store.h" #include "extensions/browser/extension_pref_value_map_factory.h" @@ -63,6 +61,19 @@ #include "shell/common/extensions/atom_extensions_client.h" #endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) || \ + BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/user_prefs/user_prefs.h" +#endif + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "base/i18n/rtl.h" +#include "components/language/core/browser/language_prefs.h" +#include "components/spellcheck/browser/pref_names.h" +#include "components/spellcheck/common/spellcheck_common.h" +#endif + using content::BrowserThread; namespace electron { @@ -102,6 +113,7 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, base::PathService::Get(DIR_APP_DATA, &path_); path_ = path_.Append(base::FilePath::FromUTF8Unsafe(GetApplicationName())); base::PathService::Override(DIR_USER_DATA, path_); + base::PathService::Override(chrome::DIR_USER_DATA, path_); } if (!in_memory && !partition.empty()) @@ -156,7 +168,10 @@ void AtomBrowserContext::InitPrefs() { ExtensionPrefValueMapFactory::GetForBrowserContext(this), IsOffTheRecord()); prefs_factory.set_extension_prefs(ext_pref_store); +#endif +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) || \ + BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) auto registry = WrapRefCounted(new user_prefs::PrefRegistrySyncable); #else auto registry = WrapRefCounted(new PrefRegistrySimple); @@ -177,13 +192,36 @@ void AtomBrowserContext::InitPrefs() { extensions::ExtensionPrefs::RegisterProfilePrefs(registry.get()); #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + BrowserContextDependencyManager::GetInstance() + ->RegisterProfilePrefsForServices(registry.get()); + + language::LanguagePrefs::RegisterProfilePrefs(registry.get()); +#endif + prefs_ = prefs_factory.Create( registry.get(), std::make_unique(weak_factory_.GetWeakPtr())); prefs_->UpdateCommandLinePrefStore(new ValueMapPrefStore); -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) || \ + BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) user_prefs::UserPrefs::Set(this, prefs_.get()); #endif + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + auto* current_dictionaries = + prefs()->Get(spellcheck::prefs::kSpellCheckDictionaries); + // No configured dictionaries, the default will be en-US + if (current_dictionaries->GetList().size() == 0) { + std::string default_code = spellcheck::GetCorrespondingSpellCheckLanguage( + base::i18n::GetConfiguredLocale()); + if (!default_code.empty()) { + base::ListValue language_codes; + language_codes.AppendString(default_code); + prefs()->Set(spellcheck::prefs::kSpellCheckDictionaries, language_codes); + } + } +#endif } void AtomBrowserContext::SetUserAgent(const std::string& user_agent) { diff --git a/shell/browser/atom_browser_main_parts.cc b/shell/browser/atom_browser_main_parts.cc index e54951818f..f59c144afa 100644 --- a/shell/browser/atom_browser_main_parts.cc +++ b/shell/browser/atom_browser_main_parts.cc @@ -103,6 +103,10 @@ #include "shell/common/extensions/atom_extensions_client.h" #endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "chrome/browser/spellchecker/spellcheck_factory.h" // nogncheck +#endif + namespace electron { namespace { @@ -442,6 +446,10 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { extensions::electron::EnsureBrowserContextKeyedServiceFactoriesBuilt(); #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + SpellcheckServiceFactory::GetInstance(); +#endif + // url::Add*Scheme are not threadsafe, this helps prevent data races. url::LockSchemeRegistries(); diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc index 45f9bf25a0..05e9bda244 100644 --- a/shell/browser/web_contents_preferences.cc +++ b/shell/browser/web_contents_preferences.cc @@ -144,6 +144,9 @@ WebContentsPreferences::WebContentsPreferences( SetDefaultBoolIfUndefined(options::kScrollBounce, false); #endif SetDefaultBoolIfUndefined(options::kOffscreen, false); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + SetDefaultBoolIfUndefined(options::kSpellcheck, true); +#endif // If this is a tag, and the embedder is offscreen-rendered, then // this WebContents is also offscreen-rendered. @@ -414,6 +417,12 @@ void WebContentsPreferences::AppendCommandLineSwitches( if (IsEnabled(options::kNodeIntegrationInSubFrames)) command_line->AppendSwitch(switches::kNodeIntegrationInSubFrames); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + if (IsEnabled(options::kSpellcheck)) { + command_line->AppendSwitch(switches::kEnableSpellcheck); + } +#endif + // We are appending args to a webContents so let's save the current state // of our preferences object so that during the lifetime of the WebContents // we can fetch the options used to initally configure the WebContents diff --git a/shell/common/gin_converters/content_converter.cc b/shell/common/gin_converters/content_converter.cc index 8c192a1863..dfc0cafb67 100644 --- a/shell/common/gin_converters/content_converter.cc +++ b/shell/common/gin_converters/content_converter.cc @@ -138,6 +138,9 @@ v8::Local Converter::ToV8( dict.Set("selectionText", params.selection_text); dict.Set("titleText", params.title_text); dict.Set("misspelledWord", params.misspelled_word); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + dict.Set("dictionarySuggestions", params.dictionary_suggestions); +#endif dict.Set("frameCharset", params.frame_charset); dict.Set("inputFieldType", params.input_field_type); dict.Set("menuSourceType", params.source_type); diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index e409c875f6..a0b0de56e5 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -173,6 +173,10 @@ const char kWebGL[] = "webgl"; // navigation. const char kNavigateOnDragDrop[] = "navigateOnDragDrop"; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +const char kSpellcheck[] = "spellcheck"; +#endif + #if BUILDFLAG(ENABLE_REMOTE_MODULE) const char kEnableRemoteModule[] = "enableRemoteModule"; #endif @@ -267,6 +271,10 @@ const char kAuthNegotiateDelegateWhitelist[] = // If set, include the port in generated Kerberos SPNs. const char kEnableAuthNegotiatePort[] = "enable-auth-negotiate-port"; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +const char kEnableSpellcheck[] = "enable-spellcheck"; +#endif + #if BUILDFLAG(ENABLE_REMOTE_MODULE) const char kEnableRemoteModule[] = "enable-remote-module"; #endif diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index e24dfa2d8f..b678f03445 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -84,6 +84,10 @@ extern const char kTextAreasAreResizable[]; extern const char kWebGL[]; extern const char kNavigateOnDragDrop[]; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +extern const char kSpellcheck[]; +#endif + #if BUILDFLAG(ENABLE_REMOTE_MODULE) extern const char kEnableRemoteModule[]; #endif @@ -134,6 +138,10 @@ extern const char kAuthServerWhitelist[]; extern const char kAuthNegotiateDelegateWhitelist[]; extern const char kEnableAuthNegotiatePort[]; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +extern const char kEnableSpellcheck[]; +#endif + #if BUILDFLAG(ENABLE_REMOTE_MODULE) extern const char kEnableRemoteModule[]; #endif diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index 50c7628014..a89cc5d8a7 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include "base/command_line.h" @@ -45,6 +46,11 @@ #include #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "components/spellcheck/renderer/spellcheck.h" +#include "components/spellcheck/renderer/spellcheck_provider.h" +#endif + #if BUILDFLAG(ENABLE_PDF_VIEWER) #include "shell/common/atom_constants.h" #endif // BUILDFLAG(ENABLE_PDF_VIEWER) @@ -148,6 +154,11 @@ void RendererClientBase::RenderThreadStarted() { thread->AddObserver(extensions_renderer_client_->GetDispatcher()); #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + if (command_line->HasSwitch(switches::kEnableSpellcheck)) + spellcheck_ = std::make_unique(®istry_, this); +#endif + blink::WebCustomElement::AddEmbedderCustomElementName("webview"); blink::WebCustomElement::AddEmbedderCustomElementName("browserplugin"); @@ -262,8 +273,37 @@ void RendererClientBase::RenderFrameCreated( dispatcher->OnRenderFrameCreated(render_frame); #endif + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + auto* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kEnableSpellcheck)) + new SpellCheckProvider(render_frame, spellcheck_.get(), this); +#endif } +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +void RendererClientBase::BindReceiverOnMainThread( + mojo::GenericPendingReceiver receiver) { + // TODO(crbug.com/977637): Get rid of the use of BinderRegistry here. This is + // only used to bind a spellcheck interface. + std::string interface_name = *receiver.interface_name(); + auto pipe = receiver.PassPipe(); + registry_.TryBindInterface(interface_name, &pipe); +} + +void RendererClientBase::GetInterface( + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) { + // TODO(crbug.com/977637): Get rid of the use of this implementation of + // |service_manager::LocalInterfaceProvider|. This was done only to avoid + // churning spellcheck code while eliminating the "chrome" and + // "chrome_renderer" services. Spellcheck is (and should remain) the only + // consumer of this implementation. + content::RenderThread::Get()->BindHostReceiver( + mojo::GenericPendingReceiver(interface_name, std::move(interface_pipe))); +} +#endif + void RendererClientBase::DidClearWindowObject( content::RenderFrame* render_frame) { // Make sure every page will get a script context created. diff --git a/shell/renderer/renderer_client_base.h b/shell/renderer/renderer_client_base.h index d06cf2508d..c6a80ab53a 100644 --- a/shell/renderer/renderer_client_base.h +++ b/shell/renderer/renderer_client_base.h @@ -19,6 +19,13 @@ #include "chrome/renderer/media/chrome_key_systems_provider.h" // nogncheck #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "services/service_manager/public/cpp/binder_registry.h" +#include "services/service_manager/public/cpp/local_interface_provider.h" + +class SpellCheck; +#endif + namespace network_hints { class PrescientNetworkingDispatcher; } @@ -35,11 +42,24 @@ namespace electron { class AtomExtensionsRendererClient; #endif -class RendererClientBase : public content::ContentRendererClient { +class RendererClientBase : public content::ContentRendererClient +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + , + public service_manager::LocalInterfaceProvider +#endif +{ public: RendererClientBase(); ~RendererClientBase() override; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + // service_manager::LocalInterfaceProvider implementation. + void GetInterface(const std::string& name, + mojo::ScopedMessagePipeHandle request_handle) override; + + void BindReceiverOnMainThread(mojo::GenericPendingReceiver receiver) override; +#endif + virtual void DidCreateScriptContext(v8::Handle context, content::RenderFrame* render_frame); virtual void WillReleaseScriptContext(v8::Handle context, @@ -108,6 +128,11 @@ class RendererClientBase : public content::ContentRendererClient { std::string renderer_client_id_; // An increasing ID used for indentifying an V8 context in this process. int64_t next_context_id_ = 0; + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + std::unique_ptr spellcheck_; + service_manager::BinderRegistry registry_; +#endif }; } // namespace electron