diff --git a/app/browser/reducers/spellCheckReducer.js b/app/browser/reducers/spellCheckReducer.js deleted file mode 100644 index 8fcfd04ebc5..00000000000 --- a/app/browser/reducers/spellCheckReducer.js +++ /dev/null @@ -1,21 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict' - -const appConstants = require('../../../js/constants/appConstants') -const tabs = require('../tabs') -const {makeImmutable} = require('../../common/state/immutableUtil') - -const spellCheckReducer = (state, action, immutableAction) => { - action = immutableAction || makeImmutable(action) - switch (action.get('actionType')) { - case appConstants.APP_ADD_WORD: - tabs.sendToAll('add-word', action.get('word')) - break - } - return state -} - -module.exports = spellCheckReducer diff --git a/app/browser/reducers/spellCheckerReducer.js b/app/browser/reducers/spellCheckerReducer.js new file mode 100644 index 00000000000..e811500e3f2 --- /dev/null +++ b/app/browser/reducers/spellCheckerReducer.js @@ -0,0 +1,66 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict' + +const appConstants = require('../../../js/constants/appConstants') +const {makeImmutable} = require('../../common/state/immutableUtil') +const {getWebContents} = require('../webContentsCache') +const spellChecker = require('../../spellChecker') + +const migrate = (state) => { + if (state.get('dictionary')) { + const addedWords = state.getIn(['dictionary', 'addedWords']) + const ignoredWords = state.getIn(['dictionary', 'ignoredWords']) + if (addedWords.size) { + addedWords.forEach((word) => { + spellChecker.addWord(word) + }) + } + if (ignoredWords.size) { + ignoredWords.forEach((word) => { + spellChecker.addWord(word) + }) + } + state = state.delete('dictionary') + } + return state +} + +const spellCheckerReducer = (state, action, immutableAction) => { + action = immutableAction || makeImmutable(action) + switch (action.get('actionType')) { + case appConstants.APP_SET_STATE: + state = migrate(state) + break + case appConstants.APP_SPELLING_SUGGESTED: + if (typeof action.get('suggestion') === 'string') { + const webContents = getWebContents(action.get('tabId')) + if (webContents && !webContents.isDestroyed()) { + webContents.replaceMisspelling(action.get('suggestion')) + } + } + break + case appConstants.APP_LEARN_SPELLING: + if (typeof action.get('word') === 'string') { + spellChecker.addWord(action.get('word')) + const webContents = getWebContents(action.get('tabId')) + if (webContents && !webContents.isDestroyed()) { + webContents.replaceMisspelling(action.get('word')) + } + } + break + case appConstants.APP_FORGET_LEARNED_SPELLING: + if (typeof action.get('word') === 'string') { + spellChecker.removeWord(action.get('word')) + const webContents = getWebContents(action.get('tabId')) + if (webContents && !webContents.isDestroyed()) { + webContents.replace(action.get('word')) + } + } + } + return state +} + +module.exports = spellCheckerReducer diff --git a/app/extensions.js b/app/extensions.js index 8b96ec25569..ec6199cb71f 100644 --- a/app/extensions.js +++ b/app/extensions.js @@ -113,7 +113,6 @@ let generateBraveManifest = () => { getBraveExtUrl('about-blank.html') + '#*' ], js: [ - 'content/scripts/spellCheck.js', 'content/scripts/themeColor.js' ] }, diff --git a/app/extensions/brave/content/scripts/spellCheck.js b/app/extensions/brave/content/scripts/spellCheck.js deleted file mode 100644 index dd25116d3c8..00000000000 --- a/app/extensions/brave/content/scripts/spellCheck.js +++ /dev/null @@ -1,22 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -let cache = {} - -chrome.ipcRenderer.on('add-word', (e, word) => { - cache[word] = true -}) - -let lang = navigator.language.split('-')[0].split('_')[0] -chrome.webFrame.setSpellCheckProvider(lang || '', true, { - spellCheck: (word) => { - if (typeof cache[word] !== 'boolean') { - const result = !chrome.ipcRenderer.sendSync('is-misspelled', word) - cache[word] = result - return result - } else { - return cache[word] - } - } -}) diff --git a/app/extensions/brave/locales/en-US/menu.properties b/app/extensions/brave/locales/en-US/menu.properties index 84a42c1d0b5..2bb55a468ed 100644 --- a/app/extensions/brave/locales/en-US/menu.properties +++ b/app/extensions/brave/locales/en-US/menu.properties @@ -155,7 +155,7 @@ extensionsManager=Extensions… zoom=Zoom new=New learnSpelling=Learn Spelling -ignoreSpelling=Ignore Spelling +forgetLearnedSpelling=Forget Learned Spelling autoHideMenuBar=Menu Bar updateChannel=Update Channel licenseText=This software uses libraries from the FFmpeg project under the LGPLv2.1 diff --git a/app/index.js b/app/index.js index 5c71816d3a0..2ee37e47f22 100644 --- a/app/index.js +++ b/app/index.js @@ -67,7 +67,6 @@ const PDFJS = require('./pdfJS') const SiteHacks = require('./siteHacks') const CmdLine = require('./cmdLine') const urlParse = require('./common/urlParse') -const spellCheck = require('./spellCheck') const locale = require('./locale') const contentSettings = require('../js/state/contentSettings') const privacy = require('../js/state/privacy') @@ -181,7 +180,6 @@ app.on('ready', () => { Autofill.init() Extensions.init() SiteHacks.init() - spellCheck.init() HttpsEverywhere.init() TrackingProtection.init() AdBlock.init() diff --git a/app/locale.js b/app/locale.js index dbf54f9b8a2..0508a3b980b 100644 --- a/app/locale.js +++ b/app/locale.js @@ -178,7 +178,7 @@ var rendererIdentifiers = function () { 'braveryStartUsingPayments', 'blockPopups', 'learnSpelling', - 'ignoreSpelling', + 'forgetLearnedSpelling', 'lookupSelection', // Other identifiers 'aboutBlankTitle', diff --git a/app/sessionStore.js b/app/sessionStore.js index c66b0b1687c..c735a6ce572 100644 --- a/app/sessionStore.js +++ b/app/sessionStore.js @@ -636,10 +636,6 @@ module.exports.defaultAppState = () => { passwords: [], notifications: [], temporarySiteSettings: {}, - dictionary: { - addedWords: [], - ignoredWords: [] - }, autofill: { addresses: { guid: [], diff --git a/app/spellCheck.js b/app/spellCheck.js deleted file mode 100644 index 5919e5ec787..00000000000 --- a/app/spellCheck.js +++ /dev/null @@ -1,90 +0,0 @@ -// @flow -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict' - -const spellchecker = require('spellchecker') -const messages = require('../js/constants/messages') -const electron = require('electron') -const ipcMain = electron.ipcMain -const app = electron.app -const appStore = require('../js/stores/appStore') -const appActions = require('../js/actions/appActions') -const contractionSet = new Set() -const getSetting = require('../js/settings').getSetting -const settings = require('../js/constants/settings') - -let dictionaryLocale - -// Stores a reference to the last added immutable words -let lastAddedWords - -const isMisspelled = (word) => - !appStore.getState().getIn(['dictionary', 'ignoredWords']).includes(word) && - !appStore.getState().getIn(['dictionary', 'addedWords']).includes(word) && - spellchecker.isMisspelled(word) - -module.exports.init = () => { - ipcMain.on(messages.IS_MISSPELLED, (e, word) => { - let misspelled = isMisspelled(word) - // If the word is misspelled and it's English, then make sure it's nt a contraction. - if (misspelled && (!dictionaryLocale || dictionaryLocale.includes('en'))) { - misspelled = !contractionSet.has(word) - } - e.returnValue = misspelled - }) - ipcMain.on(messages.GET_MISSPELLING_INFO, (e, word) => { - const misspelled = isMisspelled(word) - e.returnValue = { - isMisspelled: misspelled, - suggestions: !misspelled ? [] : spellchecker.getCorrectionsForMisspelling(word) - } - }) - - appStore.addChangeListener(() => { - let addedWords = appStore.getState().getIn(['dictionary', 'addedWords']) - if (lastAddedWords !== addedWords) { - if (lastAddedWords) { - addedWords = addedWords.splice(lastAddedWords.size) - } - addedWords.forEach(spellchecker.add.bind(spellchecker)) - lastAddedWords = appStore.getState().getIn(['dictionary', 'addedWords']) - } - }) - - // Thank you Slack team. - // NB: This is to work around tinyspeck/slack-winssb#267, where contractions - // are incorrectly marked as spelling errors. This lets people get away with - // incorrectly spelled contracted words, but it's the best we can do for now. - const contractions = [ - "ain't", "aren't", "can't", "could've", "couldn't", "couldn't've", "didn't", "doesn't", "don't", "hadn't", - "hadn't've", "hasn't", "haven't", "he'd", "he'd've", "he'll", "he's", "how'd", "how'll", "how's", "I'd", - "I'd've", "I'll", "I'm", "I've", "isn't", "it'd", "it'd've", "it'll", "it's", "let's", "ma'am", "mightn't", - "mightn't've", "might've", "mustn't", "must've", "needn't", "not've", "o'clock", "shan't", "she'd", "she'd've", - "she'll", "she's", "should've", "shouldn't", "shouldn't've", "that'll", "that's", "there'd", "there'd've", - "there're", "there's", "they'd", "they'd've", "they'll", "they're", "they've", "wasn't", "we'd", "we'd've", - "we'll", "we're", "we've", "weren't", "what'll", "what're", "what's", "what've", "when's", "where'd", - "where's", "where've", "who'd", "who'll", "who're", "who's", "who've", "why'll", "why're", "why's", "won't", - "would've", "wouldn't", "wouldn't've", "y'all", "y'all'd've", "you'd", "you'd've", "you'll", "you're", "you've" - ] - contractions.forEach((word) => contractionSet.add(word.replace(/'.*/, ''))) - - const availableDictionaries = spellchecker.getAvailableDictionaries() - let dict = (getSetting(settings.LANGUAGE) || app.getLocale()).replace('-', '_') - if (availableDictionaries.includes(dict)) { - dictionaryLocale = dict - spellchecker.setDictionary(dict) - } else { - dict = dict.split('_')[0] - if (availableDictionaries.includes(dict)) { - dictionaryLocale = dict - spellchecker.setDictionary(dict) - } - } - - if (dictionaryLocale) { - appActions.setDictionary(dictionaryLocale) - } -} diff --git a/app/spellChecker.js b/app/spellChecker.js new file mode 100644 index 00000000000..b53cd3fe504 --- /dev/null +++ b/app/spellChecker.js @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const electron = require('electron') +const session = electron.session + +module.exports.addWord = (word) => { + session.defaultSession.spellChecker.addWord(word) +} + +module.exports.removeWord = (word) => { + session.defaultSession.spellChecker.removeWord(word) +} diff --git a/docs/appActions.md b/docs/appActions.md index 937f1e32ec3..92b5ef49830 100644 --- a/docs/appActions.md +++ b/docs/appActions.md @@ -450,28 +450,6 @@ Hides a message in the notification bar -### addWord(word, learn) - -Adds a word to the dictionary - -**Parameters** - -**word**: `string`, The word to add - -**learn**: `boolean`, true if the word should be learned, false if ignored - - - -### setDictionary(locale) - -Adds a word to the dictionary - -**Parameters** - -**locale**: `string`, The locale to set for the dictionary - - - ### setLoginRequiredDetail(tabId, detail) Adds information about pending basic auth login requests diff --git a/docs/state.md b/docs/state.md index 0e457260a43..f095bbefdeb 100644 --- a/docs/state.md +++ b/docs/state.md @@ -79,11 +79,6 @@ AppStore y: number }, defaultWindowWidth: number, // DEPRECATED (0.12.7); replaced w/ defaultWindowParams.width - dictionary: { - addedWords: Array, // list of words to add to the dictionary - ignoredWords: Array, // list of words to ignore - locale: string // en_US, en, or any other locale string - }, downloads: [{ [downloadId]: { filename: string, diff --git a/js/actions/appActions.js b/js/actions/appActions.js index bb79c6a1725..66a7f64c879 100644 --- a/js/actions/appActions.js +++ b/js/actions/appActions.js @@ -548,30 +548,6 @@ const appActions = { }) }, - /** - * Adds a word to the dictionary - * @param {string} word - The word to add - * @param {boolean} learn - true if the word should be learned, false if ignored - */ - addWord: function (word, learn) { - dispatch({ - actionType: appConstants.APP_ADD_WORD, - word, - learn - }) - }, - - /** - * Adds a word to the dictionary - * @param {string} locale - The locale to set for the dictionary - */ - setDictionary: function (locale) { - dispatch({ - actionType: appConstants.APP_SET_DICTIONARY, - locale - }) - }, - /** * Adds information about pending basic auth login requests * @param {number} tabId - The tabId that generated the request @@ -1515,6 +1491,30 @@ const appActions = { windowId } }) + }, + + spellingSuggested: function (suggestion, tabId) { + dispatch({ + actionType: appConstants.APP_SPELLING_SUGGESTED, + suggestion, + tabId + }) + }, + + learnSpelling: function (word, tabId) { + dispatch({ + actionType: appConstants.APP_LEARN_SPELLING, + word, + tabId + }) + }, + + forgetLearnedSpelling: function (word, tabId) { + dispatch({ + actionType: appConstants.APP_FORGET_LEARNED_SPELLING, + word, + tabId + }) } } diff --git a/js/actions/webviewActions.js b/js/actions/webviewActions.js index 5f5a554158e..5b98fa70573 100644 --- a/js/actions/webviewActions.js +++ b/js/actions/webviewActions.js @@ -32,17 +32,6 @@ const webviewActions = { } }, - /** - * Replaces the selected text in an editable - * @param {string} text - The text to replace with - */ - replace: function (text) { - const webview = getWebview() - if (webview) { - webview.replaceMisspelling(text) - } - }, - /** * Shows the certificate infomation */ diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js index f41334fdf1e..3a2e0dea424 100644 --- a/js/constants/appConstants.js +++ b/js/constants/appConstants.js @@ -41,8 +41,6 @@ const appConstants = { APP_UPDATE_PUBLISHER_INFO: _, APP_SHOW_NOTIFICATION: _, /** @param {Object} detail */ APP_HIDE_NOTIFICATION: _, /** @param {string} message */ - APP_ADD_WORD: _, /** @param {string} word, @param {boolean} learn */ - APP_SET_DICTIONARY: _, /** @param {string} locale */ APP_BACKUP_KEYS: _, APP_RECOVER_WALLET: _, APP_ADD_AUTOFILL_ADDRESS: _, @@ -142,7 +140,10 @@ const appConstants = { APP_SWIPE_RIGHT: _, APP_ADD_BOOKMARK: _, APP_EDIT_BOOKMARK: _, - APP_DEBUG_NO_REPORT_STATE_MODE_CLICKED: _ + APP_DEBUG_NO_REPORT_STATE_MODE_CLICKED: _, + APP_SPELLING_SUGGESTED: _, + APP_LEARN_SPELLING: _, + APP_FORGET_LEARNED_SPELLING: _ } module.exports = mapValuesByKeys(appConstants) diff --git a/js/constants/messages.js b/js/constants/messages.js index 2473ce0c53c..1928552a7b9 100644 --- a/js/constants/messages.js +++ b/js/constants/messages.js @@ -76,8 +76,6 @@ const messages = { GO_FORWARD: _, RELOAD: _, DETACH: _, - IS_MISSPELLED: _, /** @arg {string} word, the word to check */ - GET_MISSPELLING_INFO: _, /** @arg {string} word, the word to lookup */ PASSWORD_DETAILS_UPDATED: _, /** @arg {Object} passwords app state */ PASSWORD_SITE_DETAILS_UPDATED: _, /** @arg {Object} passwords app state */ // Init diff --git a/js/contextMenus.js b/js/contextMenus.js index 01eb1ec0af2..6141d029182 100644 --- a/js/contextMenus.js +++ b/js/contextMenus.js @@ -541,7 +541,7 @@ function tabTemplateInit (frameProps) { return menuUtil.sanitizeTemplateItems(template) } -function getMisspelledSuggestions (selection, isMisspelled, suggestions) { +function getMisspelledSuggestions (selection, isMisspelled, suggestions, tabId) { const hasSelection = selection.length > 0 const template = [] if (hasSelection) { @@ -552,7 +552,7 @@ function getMisspelledSuggestions (selection, isMisspelled, suggestions) { return { label: suggestion, click: () => { - webviewActions.replace(suggestion) + appActions.spellingSuggested(suggestion, tabId) } } }), CommonMenu.separatorMenuItem) @@ -561,16 +561,14 @@ function getMisspelledSuggestions (selection, isMisspelled, suggestions) { template.push({ label: locale.translation('learnSpelling'), click: () => { - appActions.addWord(selection, true) - // This is needed so the underline goes away - webviewActions.replace(selection) + appActions.learnSpelling(selection, tabId) } - }, { - label: locale.translation('ignoreSpelling'), + }, CommonMenu.separatorMenuItem) + } else { + template.push({ + label: locale.translation('forgetLearnedSpelling'), click: () => { - appActions.addWord(selection, false) - // This is needed so the underline goes away - webviewActions.replace(selection) + appActions.forgetLearnedSpelling(selection, tabId) } }, CommonMenu.separatorMenuItem) } @@ -970,10 +968,16 @@ function mainTemplateInit (nodeProps, frame, tab) { if (isInputField) { let misspelledSuggestions = [] if (nodeProps.misspelledWord) { - const info = ipc.sendSync(messages.GET_MISSPELLING_INFO, nodeProps.selectionText) - if (info) { - misspelledSuggestions = getMisspelledSuggestions(nodeProps.selectionText, info.isMisspelled, info.suggestions) - } + misspelledSuggestions = + getMisspelledSuggestions(nodeProps.selectionText, + true, nodeProps.dictionarySuggestions, + frame.get('tabId')) + } else if (nodeProps.properties.hasOwnProperty('customDictionaryWord') && + nodeProps.properties['customDictionaryWord'] === nodeProps.selectionText) { + misspelledSuggestions = + getMisspelledSuggestions(nodeProps.selectionText, + false, nodeProps.dictionarySuggestions, + frame.get('tabId')) } const editableItems = getEditableItems(nodeProps.selectionText, nodeProps.editFlags, true) diff --git a/js/stores/appStore.js b/js/stores/appStore.js index 240273c4182..31260006363 100644 --- a/js/stores/appStore.js +++ b/js/stores/appStore.js @@ -397,10 +397,10 @@ const handleAppAction = (action) => { require('../../app/browser/reducers/sitesReducer'), require('../../app/browser/reducers/windowsReducer'), require('../../app/browser/reducers/syncReducer'), - require('../../app/browser/reducers/spellCheckReducer'), require('../../app/browser/reducers/clipboardReducer'), require('../../app/browser/reducers/urlBarSuggestionsReducer'), require('../../app/browser/reducers/passwordManagerReducer'), + require('../../app/browser/reducers/spellCheckerReducer'), require('../../app/browser/reducers/tabMessageBoxReducer'), require('../../app/browser/reducers/dragDropReducer'), require('../../app/browser/reducers/extensionsReducer'), @@ -647,20 +647,6 @@ const handleAppAction = (action) => { } } break - case appConstants.APP_ADD_WORD: - let listType = 'ignoredWords' - if (action.learn) { - listType = 'addedWords' - } - const path = ['dictionary', listType] - let wordList = appState.getIn(path) - if (!wordList.includes(action.word)) { - appState = appState.setIn(path, wordList.push(action.word)) - } - break - case appConstants.APP_SET_DICTIONARY: - appState = appState.setIn(['dictionary', 'locale'], action.locale) - break case appConstants.APP_LEDGER_RECOVERY_STATUS_CHANGED: { const date = new Date().getTime() diff --git a/package.json b/package.json index 361f68f908b..f017260bb9b 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,6 @@ "react-dnd": "^2.1.4", "react-dnd-html5-backend": "^2.1.2", "react-dom": "^15.5.4", - "spellchecker": "brave/node-spellchecker", "string.prototype.endswith": "^0.2.0", "string.prototype.startswith": "^0.2.0", "tablesort": "5.0.0",