Skip to content

Commit

Permalink
feat: enable builtin spellchecker (#20897)
Browse files Browse the repository at this point in the history
* 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

* chore: disable spellcheck by default in web prefs
  • Loading branch information
MarshallOfSound committed Oct 31, 2019
1 parent beff8b8 commit 40e0e8e
Show file tree
Hide file tree
Showing 34 changed files with 560 additions and 10 deletions.
29 changes: 28 additions & 1 deletion .circleci/config.yml
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
31 changes: 30 additions & 1 deletion 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")
Expand All @@ -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")
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -1265,9 +1271,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",
]
}
}
Expand Down Expand Up @@ -1353,6 +1364,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",
Expand Down
2 changes: 2 additions & 0 deletions appveyor.yml
Expand Up @@ -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
Expand All @@ -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') {
Expand Down
7 changes: 4 additions & 3 deletions build/zip.py
Expand Up @@ -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:
Expand All @@ -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:]))
1 change: 1 addition & 0 deletions buildflags/BUILD.gn
Expand Up @@ -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",
]
Expand Down
3 changes: 3 additions & 0 deletions buildflags/buildflags.gni
Expand Up @@ -33,4 +33,7 @@ declare_args() {

# Enable Chrome extensions support.
enable_electron_extensions = false

# Enable Spellchecker support
enable_builtin_spellchecker = true
}
50 changes: 50 additions & 0 deletions chromium_src/BUILD.gn
Expand Up @@ -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")
Expand Down Expand Up @@ -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",
]
}
2 changes: 2 additions & 0 deletions docs/api/browser-window.md
Expand Up @@ -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 `false`.

When setting minimum or maximum window size with `minWidth`/`maxWidth`/
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
Expand Down
28 changes: 28 additions & 0 deletions docs/api/session.md
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions docs/api/web-contents.md
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions docs/api/web-frame.md
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions electron_strings.grdp
Expand Up @@ -69,4 +69,10 @@
<message name="IDS_PICTURE_IN_PICTURE_PREVIOUS_TRACK_CONTROL_ACCESSIBLE_TEXT" desc="Accessible text label used for the controls button in the Picture-in-Picture window. The button invokes previous track action.">
Previous track
</message>
<message name="IDS_SPELLCHECK_DICTIONARY" use_name_for_id="true">
en-US
</message>
<message name="IDS_ACCEPT_LANGUAGES" use_name_for_id="true">
en-US,en
</message>
</grit-part>

0 comments on commit 40e0e8e

Please sign in to comment.