summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-20 08:26:46 +0000
committerrouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-20 08:26:46 +0000
commit6cc4a844cc9caaf721728ba0097cff61bbf7fbeb (patch)
treee9cba89d5d28430f45e8053a274635006503386a
parent9687e5ef4025dca16d787afdef2bbda6c07e2272 (diff)
downloadchromium_src-6cc4a844cc9caaf721728ba0097cff61bbf7fbeb.zip
chromium_src-6cc4a844cc9caaf721728ba0097cff61bbf7fbeb.tar.gz
chromium_src-6cc4a844cc9caaf721728ba0097cff61bbf7fbeb.tar.bz2
Enable users to alter their custom dictionary from an overlay on chrome://settings/editDictionary. This overlay can also be accessed through:
Settings |_Show advanced settings... ..|_Language and spell-checker settings... ....|_Custom spelling dictionary When a user types an unknown word into a textarea, the browser underlines this word as a misspelling. The user can right-click on this word and select "Add to Dictionary". If the user made a mistake, the user needs a way to remove a word from their custom dictionary. This CL provides this functionality. Automated tests are in https://codereview.chromium.org/11280013/ and https://codereview.chromium.org/11308076/. BUG=18238 Review URL: https://chromiumcodereview.appspot.com/11362063 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@168760 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--DEPS2
-rw-r--r--chrome/app/generated_resources.grd8
-rw-r--r--chrome/browser/resources/options/language_dictionary_overlay.css12
-rw-r--r--chrome/browser/resources/options/language_dictionary_overlay.html12
-rw-r--r--chrome/browser/resources/options/language_dictionary_overlay.js50
-rw-r--r--chrome/browser/resources/options/language_dictionary_overlay_word_list.js76
-rw-r--r--chrome/browser/resources/options/language_options.css4
-rw-r--r--chrome/browser/resources/options/language_options.html5
-rw-r--r--chrome/browser/resources/options/language_options.js10
-rw-r--r--chrome/browser/resources/options/options.html6
-rw-r--r--chrome/browser/resources/options/options.js12
-rw-r--r--chrome/browser/resources/options/options_bundle.js4
-rw-r--r--chrome/browser/spellchecker/spellcheck_custom_dictionary.cc171
-rw-r--r--chrome/browser/spellchecker/spellcheck_custom_dictionary.h58
-rw-r--r--chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc8
-rw-r--r--chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc2
-rw-r--r--chrome/browser/spellchecker/spellcheck_service.cc76
-rw-r--r--chrome/browser/spellchecker/spellcheck_service.h23
-rw-r--r--chrome/browser/ui/webui/options/core_options_handler.cc2
-rw-r--r--chrome/browser/ui/webui/options/language_dictionary_overlay_handler.cc138
-rw-r--r--chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h60
-rw-r--r--chrome/browser/ui/webui/options/options_ui.cc3
-rw-r--r--chrome/chrome_browser_ui.gypi2
-rw-r--r--chrome/common/spellcheck_messages.h5
-rw-r--r--chrome/renderer/spellchecker/cocoa_spelling_engine_mac.cc6
-rw-r--r--chrome/renderer/spellchecker/cocoa_spelling_engine_mac.h1
-rw-r--r--chrome/renderer/spellchecker/hunspell_engine.cc21
-rw-r--r--chrome/renderer/spellchecker/hunspell_engine.h8
-rw-r--r--chrome/renderer/spellchecker/spellcheck.cc10
-rw-r--r--chrome/renderer/spellchecker/spellcheck.h1
-rw-r--r--chrome/renderer/spellchecker/spelling_engine.h1
31 files changed, 684 insertions, 113 deletions
diff --git a/DEPS b/DEPS
index 2e652e0..c1ab1e5 100644
--- a/DEPS
+++ b/DEPS
@@ -81,7 +81,7 @@ deps = {
"/trunk/deps/third_party/libexif/sources@146817",
"src/third_party/hunspell":
- "/trunk/deps/third_party/hunspell@149334",
+ "/trunk/deps/third_party/hunspell@168059",
"src/third_party/hunspell_dictionaries":
"/trunk/deps/third_party/hunspell_dictionaries@168258",
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 5df91ab..fccf540 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6920,6 +6920,14 @@ Keep your key file in a safe place. You will need it to create new versions of y
Crash reporting is disabled.
</message>
+ <!-- Custom dictionary page -->
+ <message name="IDS_LANGUAGE_DICTIONARY_OVERLAY_TITLE" desc="Title for the chrome://settings/editDictionary page, where a user can add and remove words in the dictionary used for spelling.">
+ Custom spelling dictionary
+ </message>
+ <message name="IDS_LANGUAGE_DICTIONARY_OVERLAY_ADD_WORD_LABEL" desc="Label for adding a new word to the dictionary on the chrome://settings/editDictionary page.">
+ Add a new word
+ </message>
+
<!-- Instant -->
<message name="IDS_INSTANT_OPT_IN_MESSAGE" desc="Message shown in the instant confirm dialog">
With Instant enabled and if supported by your default search engine, search results appear instantly as you type queries in the omnibox, and in-line predictions help guide your search.
diff --git a/chrome/browser/resources/options/language_dictionary_overlay.css b/chrome/browser/resources/options/language_dictionary_overlay.css
new file mode 100644
index 0000000..6471212
--- /dev/null
+++ b/chrome/browser/resources/options/language_dictionary_overlay.css
@@ -0,0 +1,12 @@
+/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+#language-dictionary-overlay-page {
+ height: 300px;
+ width: 400px;
+}
+
+.dictionary-word-list-item {
+ width: 322px;
+}
diff --git a/chrome/browser/resources/options/language_dictionary_overlay.html b/chrome/browser/resources/options/language_dictionary_overlay.html
new file mode 100644
index 0000000..52432c0
--- /dev/null
+++ b/chrome/browser/resources/options/language_dictionary_overlay.html
@@ -0,0 +1,12 @@
+<div id="language-dictionary-overlay-page" class="page" hidden>
+ <div class="close-button"></div>
+ <h1 i18n-content="languageDictionaryOverlayTitle"></h1>
+ <div class="content-area">
+ <list id="language-dictionary-overlay-word-list" class="settings-list">
+ </list>
+ </div>
+ <div class="action-area button-strip">
+ <button id="language-dictionary-overlay-done-button" i18n-content="done">
+ </button>
+ </div>
+</div>
diff --git a/chrome/browser/resources/options/language_dictionary_overlay.js b/chrome/browser/resources/options/language_dictionary_overlay.js
new file mode 100644
index 0000000..653df885
--- /dev/null
+++ b/chrome/browser/resources/options/language_dictionary_overlay.js
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('options', function() {
+ var OptionsPage = options.OptionsPage;
+ var ArrayDataModel = cr.ui.ArrayDataModel;
+ var DictionaryWordsList = options.dictionary_words.DictionaryWordsList;
+
+ function EditDictionaryOverlay() {
+ OptionsPage.call(this, 'editDictionary',
+ loadTimeData.getString('languageDictionaryOverlayPage'),
+ 'language-dictionary-overlay-page');
+ }
+
+ cr.addSingletonGetter(EditDictionaryOverlay);
+
+ EditDictionaryOverlay.prototype = {
+ __proto__: OptionsPage.prototype,
+
+ wordList_: null,
+
+ initializePage: function() {
+ OptionsPage.prototype.initializePage.call(this);
+ this.wordList_ = $('language-dictionary-overlay-word-list');
+ DictionaryWordsList.decorate(this.wordList_);
+ this.wordList_.autoExpands = true;
+ $('language-dictionary-overlay-done-button').onclick = function(e) {
+ OptionsPage.closeOverlay();
+ };
+ },
+
+ didShowPage: function() {
+ chrome.send('refreshDictionaryWords');
+ },
+
+ setWordList_: function(entries) {
+ entries.push('');
+ this.wordList_.dataModel = new ArrayDataModel(entries);
+ },
+ };
+
+ EditDictionaryOverlay.setWordList = function(entries) {
+ EditDictionaryOverlay.getInstance().setWordList_(entries);
+ };
+
+ return {
+ EditDictionaryOverlay: EditDictionaryOverlay
+ };
+});
diff --git a/chrome/browser/resources/options/language_dictionary_overlay_word_list.js b/chrome/browser/resources/options/language_dictionary_overlay_word_list.js
new file mode 100644
index 0000000..c92a9c6
--- /dev/null
+++ b/chrome/browser/resources/options/language_dictionary_overlay_word_list.js
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('options.dictionary_words', function() {
+ var InlineEditableItemList = options.InlineEditableItemList;
+ var InlineEditableItem = options.InlineEditableItem;
+
+ function DictionaryWordsListItem(dictionaryWord) {
+ var el = cr.doc.createElement('div');
+ el.dictionaryWord_ = dictionaryWord;
+ DictionaryWordsListItem.decorate(el);
+ return el;
+ }
+
+ DictionaryWordsListItem.decorate = function(el) {
+ el.__proto__ = DictionaryWordsListItem.prototype;
+ el.decorate();
+ };
+
+ DictionaryWordsListItem.prototype = {
+ __proto__: InlineEditableItem.prototype,
+
+ wordField_: null,
+
+ decorate: function() {
+ InlineEditableItem.prototype.decorate.call(this);
+ if (this.dictionaryWord_ == '')
+ this.isPlaceholder = true;
+ else
+ this.editable = false;
+
+ var wordEl = this.createEditableTextCell(this.dictionaryWord_);
+ wordEl.classList.add('weakrtl');
+ wordEl.classList.add('dictionary-word-list-item');
+ this.contentElement.appendChild(wordEl);
+
+ this.wordField_ = wordEl.querySelector('input');
+ this.wordField_.classList.add('dictionary-word-list-item');
+ if (this.isPlaceholder) {
+ this.wordField_.placeholder =
+ loadTimeData.getString('languageDictionaryOverlayAddWordLabel');
+ }
+
+ this.addEventListener('commitedit', this.onEditCommitted_.bind(this));
+ },
+
+ get hasBeenEdited() {
+ return this.wordField_.value != this.dictionaryWord_;
+ },
+
+ onEditCommitted_: function(e) {
+ chrome.send('addDictionaryWord', [this.wordField_.value]);
+ },
+ };
+
+ var DictionaryWordsList = cr.ui.define('list');
+
+ DictionaryWordsList.prototype = {
+ __proto__: InlineEditableItemList.prototype,
+
+ createItem: function(dictionaryWord) {
+ return new DictionaryWordsListItem(dictionaryWord);
+ },
+
+ deleteItemAtIndex: function(index) {
+ chrome.send('removeDictionaryWord', [String(index)]);
+ },
+ };
+
+ return {
+ DictionaryWordsList: DictionaryWordsList
+ };
+
+});
+
diff --git a/chrome/browser/resources/options/language_options.css b/chrome/browser/resources/options/language_options.css
index 5a20386..3d866f3 100644
--- a/chrome/browser/resources/options/language_options.css
+++ b/chrome/browser/resources/options/language_options.css
@@ -213,3 +213,7 @@
#add-language-overlay-page .content-area {
padding-bottom: 10px;
}
+
+.standalone-link-button {
+ padding: 0;
+}
diff --git a/chrome/browser/resources/options/language_options.html b/chrome/browser/resources/options/language_options.html
index 26aac39..47e5c2e 100644
--- a/chrome/browser/resources/options/language_options.html
+++ b/chrome/browser/resources/options/language_options.html
@@ -81,6 +81,9 @@
<if expr="pp_ifdef('chromeos')">
<div i18n-content="switch_input_methods_hint"></div>
<div i18n-content="select_previous_input_method_hint"></div>
+ <button id="edit-dictionary-button"
+ class="link-button standalone-link-button"
+ i18n-content="languageDictionaryOverlayTitle"></button>
</if>
<if expr="not pp_ifdef('chromeos') and not is_macosx">
<div id="spell-check-option" class="checkbox">
@@ -89,6 +92,8 @@
metric="Options_SpellCheck" type="checkbox">
<span i18n-content="enable_spell_check"></span>
</label>
+ <button id="edit-dictionary-button" class="link-button"
+ i18n-content="languageDictionaryOverlayTitle" hidden></button>
</div>
<div id="auto-spell-correction-option" class="checkbox" hidden>
<label>
diff --git a/chrome/browser/resources/options/language_options.js b/chrome/browser/resources/options/language_options.js
index 8c388cf..f881b9c 100644
--- a/chrome/browser/resources/options/language_options.js
+++ b/chrome/browser/resources/options/language_options.js
@@ -90,6 +90,13 @@ cr.define('options', function() {
}
};
+ if (!cr.isMac) {
+ // Set up the button for editing custom spelling dictionary.
+ $('edit-dictionary-button') .onclick = function(e) {
+ OptionsPage.navigateToPage('editDictionary');
+ };
+ }
+
if (cr.isChromeOS) {
// Listen to user clicks on the add language list.
var addLanguageList = $('add-language-overlay-language-list');
@@ -691,8 +698,9 @@ cr.define('options', function() {
*/
updateEnableSpellCheck_: function() {
var value = !$('enable-spell-check').checked;
-
$('language-options-spell-check-language-button').disabled = value;
+ if (!cr.IsMac)
+ $('edit-dictionary-button').hidden = value;
},
/**
diff --git a/chrome/browser/resources/options/options.html b/chrome/browser/resources/options/options.html
index 2343a7a..6d91752 100644
--- a/chrome/browser/resources/options/options.html
+++ b/chrome/browser/resources/options/options.html
@@ -25,6 +25,9 @@
<link rel="stylesheet" href="home_page_overlay.css">
<link rel="stylesheet" href="import_data_overlay.css">
<link rel="stylesheet" href="instant_confirm_overlay.css">
+<if expr="not is_macosx">
+ <link rel="stylesheet" href="language_dictionary_overlay.css">
+</if>
<link rel="stylesheet" href="language_options.css">
<link rel="stylesheet" href="manage_profile_overlay.css">
<link rel="stylesheet" href="password_manager.css">
@@ -123,6 +126,9 @@
<include src="cookies_view_app.html">
<include src="handler_options.html">
<include src="language_add_language_overlay.html">
+ <if expr="not is_macosx">
+ <include src="language_dictionary_overlay.html">
+ </if>
<include src="media_galleries_manager_overlay.html">
<if expr="pp_ifdef('chromeos')">
<include src="chromeos/internet_detail.html">
diff --git a/chrome/browser/resources/options/options.js b/chrome/browser/resources/options/options.js
index 1a18491..7bd19d3 100644
--- a/chrome/browser/resources/options/options.js
+++ b/chrome/browser/resources/options/options.js
@@ -10,24 +10,25 @@ var AutofillOptions = options.AutofillOptions;
var BrowserOptions = options.BrowserOptions;
var ClearBrowserDataOverlay = options.ClearBrowserDataOverlay;
var ConfirmDialog = options.ConfirmDialog;
-var ContentSettings = options.ContentSettings;
var ContentSettingsExceptionsArea =
options.contentSettings.ContentSettingsExceptionsArea;
-var CookiesView = options.CookiesView;
+var ContentSettings = options.ContentSettings;
var CookiesViewApp = options.CookiesViewApp;
+var CookiesView = options.CookiesView;
+var EditDictionaryOverlay = cr.IsMac ? null : options.EditDictionaryOverlay;
var FactoryResetOverlay = options.FactoryResetOverlay;
var FontSettings = options.FontSettings;
var HandlerOptions = options.HandlerOptions;
var HomePageOverlay = options.HomePageOverlay;
var ImportDataOverlay = options.ImportDataOverlay;
var LanguageOptions = options.LanguageOptions;
+var ManageProfileOverlay = options.ManageProfileOverlay;
var MediaGalleriesManager = options.MediaGalleriesManager;
var OptionsFocusManager = options.OptionsFocusManager;
var OptionsPage = options.OptionsPage;
var PasswordManager = options.PasswordManager;
var Preferences = options.Preferences;
var PreferredNetworks = options.PreferredNetworks;
-var ManageProfileOverlay = options.ManageProfileOverlay;
var SearchEngineManager = options.SearchEngineManager;
var SearchPage = options.SearchPage;
var StartupOverlay = options.StartupOverlay;
@@ -119,6 +120,11 @@ function load() {
OptionsPage.registerOverlay(CookiesViewApp.getInstance(),
ContentSettings.getInstance(),
[$('privacyContentSettingsButton')]);
+ if (!cr.isMac) {
+ OptionsPage.registerOverlay(EditDictionaryOverlay.getInstance(),
+ LanguageOptions.getInstance(),
+ [$('edit-dictionary-button')]);
+ }
OptionsPage.registerOverlay(FontSettings.getInstance(),
BrowserOptions.getInstance(),
[$('fontSettingsCustomizeFontsButton')]);
diff --git a/chrome/browser/resources/options/options_bundle.js b/chrome/browser/resources/options/options_bundle.js
index 83f5b0a..bdbe568 100644
--- a/chrome/browser/resources/options/options_bundle.js
+++ b/chrome/browser/resources/options/options_bundle.js
@@ -79,6 +79,10 @@
<include src="home_page_overlay.js"></include>
<include src="import_data_overlay.js"></include>
<include src="language_add_language_overlay.js"></include>
+<if expr="not is_macosx">
+ <include src="language_dictionary_overlay_word_list.js"></include>
+ <include src="language_dictionary_overlay.js"></include>
+</if>
<include src="language_list.js"></include>
<include src="language_options.js"></include>
<include src="manage_profile_overlay.js"></include>
diff --git a/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc b/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
index a67596e..1d8ce84 100644
--- a/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
+++ b/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
@@ -5,18 +5,13 @@
#include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h"
#include "base/file_util.h"
-#include "base/lazy_instance.h"
#include "base/string_split.h"
-#include "base/string_util.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/spellcheck_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
-#include <functional>
-
using content::BrowserThread;
using chrome::spellcheck_common::WordList;
@@ -32,6 +27,22 @@ SpellcheckCustomDictionary::SpellcheckCustomDictionary(Profile* profile)
SpellcheckCustomDictionary::~SpellcheckCustomDictionary() {
}
+void SpellcheckCustomDictionary::Load() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ BrowserThread::PostTaskAndReplyWithResult<WordList*>(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&SpellcheckCustomDictionary::LoadDictionary,
+ base::Unretained(this)),
+ base::Bind(&SpellcheckCustomDictionary::SetCustomWordListAndDelete,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+const WordList& SpellcheckCustomDictionary::GetWords() const {
+ return words_;
+}
+
void SpellcheckCustomDictionary::LoadDictionaryIntoCustomWordList(
WordList* custom_words) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
@@ -47,12 +58,52 @@ void SpellcheckCustomDictionary::LoadDictionaryIntoCustomWordList(
mem_fun_ref(&std::string::empty)), custom_words->end());
}
-void SpellcheckCustomDictionary::Load() {
- custom_words_.clear();
- // We are not guaranteed to be on the FILE thread so post the task.
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
- &SpellcheckCustomDictionary::LoadDictionaryIntoCustomWordList,
- base::Unretained(this), &custom_words_));
+void SpellcheckCustomDictionary::SetCustomWordList(WordList* custom_words) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ words_.clear();
+ if (custom_words)
+ std::swap(words_, *custom_words);
+
+ std::vector<Observer*>::iterator it;
+ for (it = observers_.begin(); it != observers_.end(); ++it)
+ (*it)->OnCustomDictionaryLoaded();
+}
+
+bool SpellcheckCustomDictionary::AddWord(const std::string& word) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (!CustomWordAddedLocally(word))
+ return false;
+
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ base::Bind(&SpellcheckCustomDictionary::WriteWordToCustomDictionary,
+ base::Unretained(this), word));
+
+ for (content::RenderProcessHost::iterator i(
+ content::RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ i.GetCurrentValue()->Send(new SpellCheckMsg_WordAdded(word));
+ }
+
+ std::vector<Observer*>::iterator it;
+ for (it = observers_.begin(); it != observers_.end(); ++it)
+ (*it)->OnCustomDictionaryWordAdded(word);
+
+ return true;
+}
+
+bool SpellcheckCustomDictionary::CustomWordAddedLocally(
+ const std::string& word) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ WordList::iterator it = std::find(words_.begin(), words_.end(), word);
+ if (it == words_.end()) {
+ words_.push_back(word);
+ return true;
+ }
+ return false;
+ // TODO(rlp): record metrics on custom word size
}
void SpellcheckCustomDictionary::WriteWordToCustomDictionary(
@@ -72,44 +123,92 @@ void SpellcheckCustomDictionary::WriteWordToCustomDictionary(
}
}
-void SpellcheckCustomDictionary::CustomWordAddedLocally(
- const std::string& word) {
- custom_words_.push_back(word);
- // TODO(rlp): record metrics on custom word size
-}
+bool SpellcheckCustomDictionary::RemoveWord(const std::string& word) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-bool SpellcheckCustomDictionary::SetCustomWordList(WordList* custom_words) {
- if (!custom_words)
+ if (!CustomWordRemovedLocally(word))
return false;
- custom_words_.clear();
- std::swap(custom_words_, *custom_words);
+
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ base::Bind(&SpellcheckCustomDictionary::EraseWordFromCustomDictionary,
+ base::Unretained(this), word));
+
+ for (content::RenderProcessHost::iterator i(
+ content::RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ i.GetCurrentValue()->Send(new SpellCheckMsg_WordRemoved(word));
+ }
+
+ std::vector<Observer*>::iterator it;
+ for (it = observers_.begin(); it != observers_.end(); ++it)
+ (*it)->OnCustomDictionaryWordRemoved(word);
+
return true;
}
-const WordList& SpellcheckCustomDictionary::GetCustomWords() const {
- return custom_words_;
+bool SpellcheckCustomDictionary::CustomWordRemovedLocally(
+ const std::string& word) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ WordList::iterator it = std::find(words_.begin(), words_.end(), word);
+ if (it != words_.end()) {
+ words_.erase(it);
+ return true;
+ }
+ return false;
}
-void SpellcheckCustomDictionary::AddWord(const std::string& word) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+void SpellcheckCustomDictionary::EraseWordFromCustomDictionary(
+ const std::string& word) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(IsStringUTF8(word));
- CustomWordAddedLocally(word);
+ WordList custom_words;
+ LoadDictionaryIntoCustomWordList(&custom_words);
+
+ char empty[] = {'\0'};
+ char separator[] = {'\n', '\0'};
+ file_util::WriteFile(custom_dictionary_path_, empty, 0);
+ for (WordList::iterator it = custom_words.begin();
+ it != custom_words.end();
+ ++it) {
+ std::string word_to_add = *it;
+ if (word.compare(word_to_add) != 0) {
+ file_util::AppendToFile(custom_dictionary_path_, word_to_add.c_str(),
+ word_to_add.length());
+ file_util::AppendToFile(custom_dictionary_path_, separator, 1);
+ }
+ }
+}
- BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE,
- base::Bind(&SpellcheckCustomDictionary::WriteWordToCustomDictionary,
- base::Unretained(this), word),
- base::Bind(&SpellcheckCustomDictionary::AddWordComplete,
- weak_ptr_factory_.GetWeakPtr(), word));
+void SpellcheckCustomDictionary::AddObserver(Observer* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ observers_.push_back(observer);
}
-void SpellcheckCustomDictionary::AddWordComplete(const std::string& word) {
+void SpellcheckCustomDictionary::RemoveObserver(Observer* observer) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- for (content::RenderProcessHost::iterator i(
- content::RenderProcessHost::AllHostsIterator());
- !i.IsAtEnd(); i.Advance()) {
- i.GetCurrentValue()->Send(new SpellCheckMsg_WordAdded(word));
- }
+ std::vector<Observer*>::iterator it = std::find(observers_.begin(),
+ observers_.end(),
+ observer);
+ if (it != observers_.end())
+ observers_.erase(it);
}
+WordList* SpellcheckCustomDictionary::LoadDictionary() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ WordList* custom_words = new WordList;
+ LoadDictionaryIntoCustomWordList(custom_words);
+ return custom_words;
+}
+void SpellcheckCustomDictionary::SetCustomWordListAndDelete(
+ WordList* custom_words) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ SetCustomWordList(custom_words);
+ delete custom_words;
+}
diff --git a/chrome/browser/spellchecker/spellcheck_custom_dictionary.h b/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
index 939a349..1c244e7 100644
--- a/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
+++ b/chrome/browser/spellchecker/spellcheck_custom_dictionary.h
@@ -17,37 +17,71 @@
// Defines a custom dictionary which users can add their own words to.
class SpellcheckCustomDictionary : public SpellcheckDictionary {
public:
+ class Observer {
+ public:
+ virtual void OnCustomDictionaryLoaded() = 0;
+ virtual void OnCustomDictionaryWordAdded(const std::string& word) = 0;
+ virtual void OnCustomDictionaryWordRemoved(const std::string& word) = 0;
+ };
+
explicit SpellcheckCustomDictionary(Profile* profile);
virtual ~SpellcheckCustomDictionary();
+ // Overridden from SpellcheckDictionary:
virtual void Load() OVERRIDE;
- void WriteWordToCustomDictionary(const std::string& word);
- // Returns true if successful. False otherwise. Takes ownership of
- // custom_words and replaces pointer with an empty vector.
- bool SetCustomWordList(chrome::spellcheck_common::WordList* custom_words);
- const chrome::spellcheck_common::WordList& GetCustomWords() const;
- void CustomWordAddedLocally(const std::string& word);
+ const chrome::spellcheck_common::WordList& GetWords() const;
+
void LoadDictionaryIntoCustomWordList(
chrome::spellcheck_common::WordList* custom_words);
+ // Moves the words from the |custom_words| argument into its own private
+ // member variable. Does not delete the memory at |custom_words|.
+ void SetCustomWordList(chrome::spellcheck_common::WordList* custom_words);
+
// Adds the given word to the custom words list and inform renderer of the
- // update.
- void AddWord(const std::string& word);
+ // update. Returns false for duplicate words.
+ bool AddWord(const std::string& word);
+
+ // Returns false for duplicate words.
+ bool CustomWordAddedLocally(const std::string& word);
+
+ void WriteWordToCustomDictionary(const std::string& word);
- // The reply point for PostTaskAndReply. Called when AddWord is finished
- // adding a word in the background.
- void AddWordComplete(const std::string& word);
+ // Removes the given word from the custom words list and inform renderer of
+ // the update. Returns false for words that are not in the dictionary.
+ bool RemoveWord(const std::string& word);
+
+ // Returns false for words that are not in the dictionary.
+ bool CustomWordRemovedLocally(const std::string& word);
+
+ void EraseWordFromCustomDictionary(const std::string& word);
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
private:
+ // Returns a newly allocated list of words read from custom dictionary file.
+ // The caller owns this new list.
+ chrome::spellcheck_common::WordList* LoadDictionary();
+
+ // The reply point for PostTaskAndReplyWithResult. Called when LoadDictionary
+ // is finished reading words from custom dictionary file. Moves the strings
+ // from the |custom_words| argument into the private member variable |words_|
+ // and deletes the memory at |custom_words|.
+ void SetCustomWordListAndDelete(
+ chrome::spellcheck_common::WordList* custom_words);
+
// In-memory cache of the custom words file.
- chrome::spellcheck_common::WordList custom_words_;
+ chrome::spellcheck_common::WordList words_;
// A path for custom dictionary per profile.
FilePath custom_dictionary_path_;
base::WeakPtrFactory<SpellcheckCustomDictionary> weak_ptr_factory_;
+ std::vector<Observer*> observers_;
+
DISALLOW_COPY_AND_ASSIGN(SpellcheckCustomDictionary);
};
diff --git a/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc b/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc
index a3e66d3..b5fb425 100644
--- a/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc
+++ b/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc
@@ -57,7 +57,7 @@ TEST_F(SpellcheckCustomDictionaryTest, SpellcheckSetCustomWordList) {
SpellcheckCustomDictionary* custom_dictionary =
spellcheck_service->GetCustomDictionary();
custom_dictionary->SetCustomWordList(&loaded_custom_words);
- EXPECT_EQ(custom_dictionary->GetCustomWords(), expected);
+ EXPECT_EQ(custom_dictionary->GetWords(), expected);
}
TEST_F(SpellcheckCustomDictionaryTest, CustomWordAddedLocally) {
@@ -69,13 +69,13 @@ TEST_F(SpellcheckCustomDictionaryTest, CustomWordAddedLocally) {
spellcheck_service->GetCustomDictionary();
custom_dictionary->Load();
WordList expected;
- EXPECT_EQ(custom_dictionary->GetCustomWords(), expected);
+ EXPECT_EQ(custom_dictionary->GetWords(), expected);
custom_dictionary->CustomWordAddedLocally("foo");
expected.push_back("foo");
- EXPECT_EQ(custom_dictionary->GetCustomWords(), expected);
+ EXPECT_EQ(custom_dictionary->GetWords(), expected);
custom_dictionary->CustomWordAddedLocally("bar");
expected.push_back("bar");
- EXPECT_EQ(custom_dictionary->GetCustomWords(), expected);
+ EXPECT_EQ(custom_dictionary->GetWords(), expected);
}
TEST_F(SpellcheckCustomDictionaryTest, SaveAndLoad) {
diff --git a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
index 9f6fca0..1c27cbb 100644
--- a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
+++ b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
@@ -281,7 +281,7 @@ bool SpellcheckHunspellDictionary::IsReady() const {
}
void SpellcheckHunspellDictionary::InformProfileOfInitialization() {
- spellcheck_service_->InformProfileOfInitializationWithCustomWords(NULL);
+ spellcheck_service_->InitForAllRenderers();
}
const base::PlatformFile&
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index f0f76d1..62af03f 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -50,6 +50,7 @@ SpellcheckService::SpellcheckService(Profile* profile)
hunspell_dictionary_->Load();
custom_dictionary_.reset(new SpellcheckCustomDictionary(profile_));
+ custom_dictionary_->AddObserver(this);
custom_dictionary_->Load();
registrar_.Add(weak_ptr_factory_.GetWeakPtr(),
@@ -130,27 +131,22 @@ bool SpellcheckService::SignalStatusEvent(
return true;
}
-// static
-void SpellcheckService::AttachStatusEvent(base::WaitableEvent* status_event) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- g_status_event = status_event;
-}
-
-// static
-SpellcheckService::EventType SpellcheckService::WaitStatusEvent() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (g_status_event)
- g_status_event->Wait();
- return g_status_type;
-}
-
void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled) {
metrics_.reset(new SpellCheckHostMetrics());
metrics_->RecordEnabledStats(spellcheck_enabled);
}
+void SpellcheckService::InitForAllRenderers() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ for (content::RenderProcessHost::iterator i(
+ content::RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ content::RenderProcessHost* process = i.GetCurrentValue();
+ if (process)
+ InitForRenderer(process);
+ }
+}
+
void SpellcheckService::InitForRenderer(content::RenderProcessHost* process) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -178,15 +174,21 @@ void SpellcheckService::InitForRenderer(content::RenderProcessHost* process) {
#endif
}
- WordList custom_words = GetCustomDictionary()->GetCustomWords();
-
process->Send(new SpellCheckMsg_Init(
file,
- custom_words,
+ custom_dictionary_->GetWords(),
hunspell_dictionary_->GetLanguage(),
prefs->GetBoolean(prefs::kEnableAutoSpellCorrect)));
}
+SpellCheckHostMetrics* SpellcheckService::GetMetrics() const {
+ return metrics_.get();
+}
+
+SpellcheckCustomDictionary* SpellcheckService::GetCustomDictionary() {
+ return custom_dictionary_.get();
+}
+
void SpellcheckService::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
@@ -201,7 +203,7 @@ void SpellcheckService::OnPreferenceChanged(PrefServiceBase* prefs,
DCHECK(prefs);
if (pref_name_in == prefs::kSpellCheckDictionary ||
pref_name_in == prefs::kEnableSpellCheck) {
- InformProfileOfInitializationWithCustomWords(NULL);
+ InitForAllRenderers();
} else if (pref_name_in == prefs::kEnableAutoSpellCorrect) {
bool enabled = prefs->GetBoolean(prefs::kEnableAutoSpellCorrect);
for (content::RenderProcessHost::iterator i(
@@ -213,28 +215,28 @@ void SpellcheckService::OnPreferenceChanged(PrefServiceBase* prefs,
}
}
-SpellCheckHostMetrics* SpellcheckService::GetMetrics() const {
- return metrics_.get();
+void SpellcheckService::OnCustomDictionaryLoaded() {
+ InitForAllRenderers();
}
-SpellcheckCustomDictionary* SpellcheckService::GetCustomDictionary() {
- return custom_dictionary_.get();
+void SpellcheckService::OnCustomDictionaryWordAdded(const std::string& word) {
+}
+
+void SpellcheckService::OnCustomDictionaryWordRemoved(const std::string& word) {
}
-// TODO(rlp): rename to something more logical.
-void SpellcheckService::InformProfileOfInitializationWithCustomWords(
- WordList* custom_words) {
+// static
+void SpellcheckService::AttachStatusEvent(base::WaitableEvent* status_event) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (custom_words) {
- GetCustomDictionary()->SetCustomWordList(custom_words);
- }
+ g_status_event = status_event;
+}
- for (content::RenderProcessHost::iterator i(
- content::RenderProcessHost::AllHostsIterator());
- !i.IsAtEnd(); i.Advance()) {
- content::RenderProcessHost* process = i.GetCurrentValue();
- if (process)
- InitForRenderer(process);
- }
+// static
+SpellcheckService::EventType SpellcheckService::WaitStatusEvent() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ if (g_status_event)
+ g_status_event->Wait();
+ return g_status_type;
}
diff --git a/chrome/browser/spellchecker/spellcheck_service.h b/chrome/browser/spellchecker/spellcheck_service.h
index 3c0ffff..b0bbe77 100644
--- a/chrome/browser/spellchecker/spellcheck_service.h
+++ b/chrome/browser/spellchecker/spellcheck_service.h
@@ -34,7 +34,8 @@ class RenderProcessHost;
// SpellCheckService maintains any per-profile information about spellcheck.
class SpellcheckService : public ProfileKeyedService,
public PrefObserver,
- public content::NotificationObserver {
+ public content::NotificationObserver,
+ public SpellcheckCustomDictionary::Observer {
public:
// Event types used for reporting the status of this class and its derived
// classes to browser tests.
@@ -67,12 +68,13 @@ class SpellcheckService : public ProfileKeyedService,
// when we do not set an event to |status_event_|.
static bool SignalStatusEvent(EventType type);
- void Initialize();
-
// Instantiates SpellCheckHostMetrics object and makes it ready for recording
// metrics. This should be called only if the metrics recording is active.
void StartRecordingMetrics(bool spellcheck_enabled);
+ // Pass all renderers some basic initialization infomration.
+ void InitForAllRenderers();
+
// Pass the renderer some basic intialization information. Note that the
// renderer will not load Hunspell until it needs to.
void InitForRenderer(content::RenderProcessHost* process);
@@ -81,16 +83,9 @@ class SpellcheckService : public ProfileKeyedService,
// or null when metrics recording is disabled.
SpellCheckHostMetrics* GetMetrics() const;
- // Returns the instance of the custom dictionary. Custom dictionary
- // will be lazily initialized.
+ // Returns the instance of the custom dictionary.
SpellcheckCustomDictionary* GetCustomDictionary();
- // Inform |profile_| that initialization has finished.
- // |custom_words| holds the custom word list which was
- // loaded at the file thread.
- void InformProfileOfInitializationWithCustomWords(
- chrome::spellcheck_common::WordList* custom_words);
-
// NotificationProfile implementation.
virtual void Observe(int type,
const content::NotificationSource& source,
@@ -99,6 +94,12 @@ class SpellcheckService : public ProfileKeyedService,
// PrefObserver implementation.
virtual void OnPreferenceChanged(PrefServiceBase* service,
const std::string& pref_name) OVERRIDE;
+
+ // SpellcheckCustomDictionary::Observer implementation.
+ virtual void OnCustomDictionaryLoaded() OVERRIDE;
+ virtual void OnCustomDictionaryWordAdded(const std::string& word) OVERRIDE;
+ virtual void OnCustomDictionaryWordRemoved(const std::string& word) OVERRIDE;
+
private:
FRIEND_TEST_ALL_PREFIXES(SpellcheckServiceBrowserTest, DeleteCorruptedBDICT);
diff --git a/chrome/browser/ui/webui/options/core_options_handler.cc b/chrome/browser/ui/webui/options/core_options_handler.cc
index a49a69b..3a07265 100644
--- a/chrome/browser/ui/webui/options/core_options_handler.cc
+++ b/chrome/browser/ui/webui/options/core_options_handler.cc
@@ -121,6 +121,8 @@ void CoreOptionsHandler::GetStaticLocalizedValues(
l10n_util::GetStringUTF16(IDS_LEARN_MORE));
localized_strings->SetString("close",
l10n_util::GetStringUTF16(IDS_CLOSE));
+ localized_strings->SetString("done",
+ l10n_util::GetStringUTF16(IDS_DONE));
}
void CoreOptionsHandler::Uninitialize() {
diff --git a/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.cc b/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.cc
new file mode 100644
index 0000000..2f86de4
--- /dev/null
+++ b/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "content/public/browser/web_ui.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace options {
+
+namespace {
+bool StringCompare(const std::string& a, const std::string& b) {
+ return a.compare(b) < 0;
+}
+} // namespace
+
+LanguageDictionaryOverlayHandler::LanguageDictionaryOverlayHandler()
+ : overlay_initialized_(false),
+ dictionary_(NULL) {
+}
+
+LanguageDictionaryOverlayHandler::~LanguageDictionaryOverlayHandler() {
+}
+
+void LanguageDictionaryOverlayHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ RegisterTitle(localized_strings,
+ "languageDictionaryOverlayPage",
+ IDS_LANGUAGE_DICTIONARY_OVERLAY_TITLE);
+ localized_strings->SetString(
+ "languageDictionaryOverlayTitle",
+ l10n_util::GetStringUTF16(IDS_LANGUAGE_DICTIONARY_OVERLAY_TITLE));
+ localized_strings->SetString(
+ "languageDictionaryOverlayAddWordLabel",
+ l10n_util::GetStringUTF16(
+ IDS_LANGUAGE_DICTIONARY_OVERLAY_ADD_WORD_LABEL));
+}
+
+void LanguageDictionaryOverlayHandler::InitializeHandler() {
+ dictionary_ = SpellcheckServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()))->GetCustomDictionary();
+ dictionary_->AddObserver(this);
+}
+
+void LanguageDictionaryOverlayHandler::InitializePage() {
+}
+
+void LanguageDictionaryOverlayHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "addDictionaryWord",
+ base::Bind(&LanguageDictionaryOverlayHandler::AddWord,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeDictionaryWord",
+ base::Bind(&LanguageDictionaryOverlayHandler::RemoveWord,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "refreshDictionaryWords",
+ base::Bind(&LanguageDictionaryOverlayHandler::RefreshWords,
+ base::Unretained(this)));
+}
+
+void LanguageDictionaryOverlayHandler::Uninitialize() {
+ overlay_initialized_ = false;
+ if (dictionary_)
+ dictionary_->RemoveObserver(this);
+}
+
+void LanguageDictionaryOverlayHandler::OnCustomDictionaryLoaded() {
+ ResetDataModel();
+ UpdateWordList();
+}
+
+void LanguageDictionaryOverlayHandler::OnCustomDictionaryWordAdded(
+ const std::string& word) {
+}
+
+void LanguageDictionaryOverlayHandler::OnCustomDictionaryWordRemoved(
+ const std::string& word) {
+}
+
+void LanguageDictionaryOverlayHandler::ResetDataModel() {
+ // TODO(rouslan): Paginate dictionary words.
+ data_model_ = dictionary_->GetWords();
+ sort(data_model_.begin(), data_model_.end(), StringCompare);
+}
+
+void LanguageDictionaryOverlayHandler::UpdateWordList() {
+ if (!overlay_initialized_)
+ return;
+ ListValue list_value;
+ list_value.AppendStrings(data_model_);
+ web_ui()->CallJavascriptFunction("EditDictionaryOverlay.setWordList",
+ list_value);
+}
+
+void LanguageDictionaryOverlayHandler::RefreshWords(const ListValue* args) {
+ overlay_initialized_ = true;
+ ResetDataModel();
+ UpdateWordList();
+}
+
+void LanguageDictionaryOverlayHandler::AddWord(const ListValue* args) {
+ std::string new_word;
+ if (!args->GetString(0, &new_word) || new_word.empty()) {
+ NOTREACHED();
+ return;
+ }
+ dictionary_->AddWord(new_word);
+ data_model_.push_back(new_word);
+ UpdateWordList();
+}
+
+void LanguageDictionaryOverlayHandler::RemoveWord(const ListValue* args) {
+ std::string index_string;
+ if (!args->GetString(0, &index_string) || index_string.empty()) {
+ NOTREACHED();
+ return;
+ }
+ std::stringstream ss(index_string);
+ int i = -1;
+ if ((ss >> i).fail() || i < 0 || i >= (int) data_model_.size()) {
+ NOTREACHED();
+ return;
+ }
+ dictionary_->RemoveWord(data_model_.at(i));
+ data_model_.erase(data_model_.begin() + i);
+ UpdateWordList();
+}
+
+} // namespace options
diff --git a/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h b/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h
new file mode 100644
index 0000000..4c9fae2
--- /dev/null
+++ b/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_DICTIONARY_OVERLAY_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_DICTIONARY_OVERLAY_HANDLER_H_
+
+#include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace options {
+
+class LanguageDictionaryOverlayHandler
+ : public OptionsPageUIHandler,
+ public SpellcheckCustomDictionary::Observer {
+ public:
+ LanguageDictionaryOverlayHandler();
+ virtual ~LanguageDictionaryOverlayHandler();
+
+ // Overridden from OptionsPageUIHandler:
+ virtual void GetLocalizedValues(
+ base::DictionaryValue* localized_strings) OVERRIDE;
+ virtual void InitializeHandler() OVERRIDE;
+ virtual void InitializePage() OVERRIDE;
+ virtual void RegisterMessages() OVERRIDE;
+ virtual void Uninitialize() OVERRIDE;
+
+ // Overridden from SpellcheckCustomDictionary::Observer:
+ virtual void OnCustomDictionaryLoaded() OVERRIDE;
+ virtual void OnCustomDictionaryWordAdded(const std::string& word) OVERRIDE;
+ virtual void OnCustomDictionaryWordRemoved(const std::string& word) OVERRIDE;
+
+ private:
+ // Resets the data model with the words from the dictionary.
+ void ResetDataModel();
+
+ // Calls WebUI to update the dictionary words.
+ void UpdateWordList();
+
+ // Refreshes the dictionary words. Called from WebUI.
+ void RefreshWords(const base::ListValue* args);
+
+ // Adds a new word to the dictionary. Called from WebUI.
+ void AddWord(const base::ListValue* args);
+
+ // Removes a word from the dictionary. Called from WebUI.
+ void RemoveWord(const base::ListValue* args);
+
+ // Whether the overlay is initialized and ready to show data.
+ bool overlay_initialized_;
+
+ SpellcheckCustomDictionary* dictionary_;
+ std::vector<std::string> data_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(LanguageDictionaryOverlayHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_DICTIONARY_OVERLAY_HANDLER_H_
diff --git a/chrome/browser/ui/webui/options/options_ui.cc b/chrome/browser/ui/webui/options/options_ui.cc
index 567122f..81e6db5 100644
--- a/chrome/browser/ui/webui/options/options_ui.cc
+++ b/chrome/browser/ui/webui/options/options_ui.cc
@@ -33,6 +33,7 @@
#include "chrome/browser/ui/webui/options/handler_options_handler.h"
#include "chrome/browser/ui/webui/options/home_page_overlay_handler.h"
#include "chrome/browser/ui/webui/options/import_data_handler.h"
+#include "chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h"
#include "chrome/browser/ui/webui/options/language_options_handler.h"
#include "chrome/browser/ui/webui/options/manage_profile_handler.h"
#include "chrome/browser/ui/webui/options/media_devices_selection_handler.h"
@@ -249,6 +250,8 @@ OptionsUI::OptionsUI(content::WebUI* web_ui)
#else
AddOptionsPageUIHandler(localized_strings, new LanguageOptionsHandler());
#endif
+ AddOptionsPageUIHandler(localized_strings,
+ new LanguageDictionaryOverlayHandler());
AddOptionsPageUIHandler(localized_strings, new ManageProfileHandler());
AddOptionsPageUIHandler(localized_strings, new PasswordManagerHandler());
AddOptionsPageUIHandler(localized_strings, new SearchEngineManagerHandler());
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 54e8439..d57dd6e 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -1981,6 +1981,8 @@
'browser/ui/webui/options/home_page_overlay_handler.h',
'browser/ui/webui/options/import_data_handler.cc',
'browser/ui/webui/options/import_data_handler.h',
+ 'browser/ui/webui/options/language_dictionary_overlay_handler.cc',
+ 'browser/ui/webui/options/language_dictionary_overlay_handler.h',
'browser/ui/webui/options/language_options_handler.cc',
'browser/ui/webui/options/language_options_handler.h',
'browser/ui/webui/options/language_options_handler_common.cc',
diff --git a/chrome/common/spellcheck_messages.h b/chrome/common/spellcheck_messages.h
index a617814..75dcc1c 100644
--- a/chrome/common/spellcheck_messages.h
+++ b/chrome/common/spellcheck_messages.h
@@ -39,6 +39,11 @@ IPC_MESSAGE_CONTROL4(SpellCheckMsg_Init,
IPC_MESSAGE_CONTROL1(SpellCheckMsg_WordAdded,
std::string /* word */)
+// A word has been removed from the custom dictionary; update the local custom
+// word list.
+IPC_MESSAGE_CONTROL1(SpellCheckMsg_WordRemoved,
+ std::string /* word */)
+
// Toggle the auto spell correct functionality.
IPC_MESSAGE_CONTROL1(SpellCheckMsg_EnableAutoSpellCorrect,
bool /* enable */)
diff --git a/chrome/renderer/spellchecker/cocoa_spelling_engine_mac.cc b/chrome/renderer/spellchecker/cocoa_spelling_engine_mac.cc
index 9c3ba9d..463940e 100644
--- a/chrome/renderer/spellchecker/cocoa_spelling_engine_mac.cc
+++ b/chrome/renderer/spellchecker/cocoa_spelling_engine_mac.cc
@@ -43,5 +43,9 @@ void CocoaSpellingEngine::FillSuggestionList(
}
void CocoaSpellingEngine::OnWordAdded(const std::string&) {
- // OSX doesn't support the custom dictionary.
+ // OSX doesn't support the custom dictionary yet.
+}
+
+void CocoaSpellingEngine::OnWordRemoved(const std::string&) {
+ // OSX doesn't support the custom dictionary yet.
}
diff --git a/chrome/renderer/spellchecker/cocoa_spelling_engine_mac.h b/chrome/renderer/spellchecker/cocoa_spelling_engine_mac.h
index 1b5183b..c2856c6 100644
--- a/chrome/renderer/spellchecker/cocoa_spelling_engine_mac.h
+++ b/chrome/renderer/spellchecker/cocoa_spelling_engine_mac.h
@@ -16,6 +16,7 @@ class CocoaSpellingEngine : public SpellingEngine {
virtual void FillSuggestionList(const string16& wrong_word,
std::vector<string16>* optional_suggestions) OVERRIDE;
virtual void OnWordAdded(const std::string& word) OVERRIDE;
+ virtual void OnWordRemoved(const std::string& word) OVERRIDE;
};
#endif // CHROME_RENDERER_SPELLCHECKER_NSSPELLCHECKER_ENGINE_H_
diff --git a/chrome/renderer/spellchecker/hunspell_engine.cc b/chrome/renderer/spellchecker/hunspell_engine.cc
index 9b352d5..71a14d2 100644
--- a/chrome/renderer/spellchecker/hunspell_engine.cc
+++ b/chrome/renderer/spellchecker/hunspell_engine.cc
@@ -56,10 +56,9 @@ void HunspellEngine::InitializeHunspell() {
new Hunspell(bdict_file_->data(), bdict_file_->length()));
// Add custom words to Hunspell.
- for (std::vector<std::string>::iterator it = custom_words_.begin();
- it != custom_words_.end(); ++it) {
+ chrome::spellcheck_common::WordList::iterator it;
+ for (it = custom_words_.begin(); it != custom_words_.end(); ++it)
AddWordToHunspell(*it);
- }
DHISTOGRAM_TIMES("Spellcheck.InitTime",
base::Histogram::DebugNow() - debug_start_time);
@@ -73,6 +72,11 @@ void HunspellEngine::AddWordToHunspell(const std::string& word) {
hunspell_->add(word.c_str());
}
+void HunspellEngine::RemoveWordFromHunspell(const std::string& word) {
+ if (!word.empty() && word.length() < MAXWORDLEN)
+ hunspell_->remove(word.c_str());
+}
+
bool HunspellEngine::CheckSpelling(const string16& word_to_check, int tag) {
bool word_correct = false;
std::string word_to_check_utf8(UTF16ToUTF8(word_to_check));
@@ -124,6 +128,17 @@ void HunspellEngine::OnWordAdded(const std::string& word) {
}
}
+void HunspellEngine::OnWordRemoved(const std::string& word) {
+ if (!hunspell_.get()) {
+ chrome::spellcheck_common::WordList::iterator it = std::find(
+ custom_words_.begin(), custom_words_.end(), word);
+ if (it != custom_words_.end())
+ custom_words_.erase(it);
+ } else {
+ RemoveWordFromHunspell(word);
+ }
+}
+
bool HunspellEngine::InitializeIfNeeded() {
if (!initialized_ && !dictionary_requested_) {
// RenderThread will not exist in test.
diff --git a/chrome/renderer/spellchecker/hunspell_engine.h b/chrome/renderer/spellchecker/hunspell_engine.h
index 0d91b24..f96e5de 100644
--- a/chrome/renderer/spellchecker/hunspell_engine.h
+++ b/chrome/renderer/spellchecker/hunspell_engine.h
@@ -9,6 +9,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
+#include "chrome/common/spellcheck_common.h"
#include "chrome/renderer/spellchecker/spelling_engine.h"
#include <string>
@@ -30,6 +31,8 @@ class HunspellEngine : public SpellingEngine {
virtual void FillSuggestionList(const string16& wrong_word,
std::vector<string16>* optional_suggestions) OVERRIDE;
virtual void OnWordAdded(const std::string& word) OVERRIDE;
+ virtual void OnWordRemoved(const std::string& word) OVERRIDE;
+
private:
// Initializes the Hunspell dictionary, or does nothing if |hunspell_| is
// non-null. This blocks.
@@ -38,13 +41,16 @@ class HunspellEngine : public SpellingEngine {
// Add the given custom word to |hunspell_|.
void AddWordToHunspell(const std::string& word);
+ // Remove the given custom word from |hunspell_|.
+ void RemoveWordFromHunspell(const std::string& word);
+
// We memory-map the BDict file.
scoped_ptr<file_util::MemoryMappedFile> bdict_file_;
// The hunspell dictionary in use.
scoped_ptr<Hunspell> hunspell_;
- std::vector<std::string> custom_words_;
+ chrome::spellcheck_common::WordList custom_words_;
base::PlatformFile file_;
diff --git a/chrome/renderer/spellchecker/spellcheck.cc b/chrome/renderer/spellchecker/spellcheck.cc
index d289548..01eec7e 100644
--- a/chrome/renderer/spellchecker/spellcheck.cc
+++ b/chrome/renderer/spellchecker/spellcheck.cc
@@ -56,6 +56,7 @@ bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(SpellCheck, message)
IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit)
IPC_MESSAGE_HANDLER(SpellCheckMsg_WordAdded, OnWordAdded)
+ IPC_MESSAGE_HANDLER(SpellCheckMsg_WordRemoved, OnWordRemoved)
IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect,
OnEnableAutoSpellCorrect)
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -77,10 +78,15 @@ void SpellCheck::OnInit(IPC::PlatformFileForTransit bdict_file,
}
void SpellCheck::OnWordAdded(const std::string& word) {
- if (platform_spelling_engine_)
+ if (platform_spelling_engine_.get())
platform_spelling_engine_->OnWordAdded(word);
}
+void SpellCheck::OnWordRemoved(const std::string& word) {
+ if (platform_spelling_engine_.get())
+ platform_spelling_engine_->OnWordRemoved(word);
+}
+
void SpellCheck::OnEnableAutoSpellCorrect(bool enable) {
auto_spell_correct_turned_on_ = enable;
}
@@ -124,7 +130,7 @@ bool SpellCheck::SpellCheckWord(
return true;
// Do nothing if spell checking is disabled.
- if (!platform_spelling_engine_ ||
+ if (!platform_spelling_engine_.get() ||
!platform_spelling_engine_->IsEnabled())
return true;
diff --git a/chrome/renderer/spellchecker/spellcheck.h b/chrome/renderer/spellchecker/spellcheck.h
index 6292005..7c32d66 100644
--- a/chrome/renderer/spellchecker/spellcheck.h
+++ b/chrome/renderer/spellchecker/spellcheck.h
@@ -110,6 +110,7 @@ class SpellCheck : public content::RenderProcessObserver,
const std::string& language,
bool auto_spell_correct);
void OnWordAdded(const std::string& word);
+ void OnWordRemoved(const std::string& word);
void OnEnableAutoSpellCorrect(bool enable);
// If there is no dictionary file, then this requests one from the browser
diff --git a/chrome/renderer/spellchecker/spelling_engine.h b/chrome/renderer/spellchecker/spelling_engine.h
index 13d3b28..0901843 100644
--- a/chrome/renderer/spellchecker/spelling_engine.h
+++ b/chrome/renderer/spellchecker/spelling_engine.h
@@ -24,6 +24,7 @@ class SpellingEngine {
virtual void FillSuggestionList(const string16& wrong_word,
std::vector<string16>* optional_suggestions) = 0;
virtual void OnWordAdded(const std::string& word) = 0;
+ virtual void OnWordRemoved(const std::string& word) = 0;
};
#endif // CHROME_RENDERER_SPELLCHECKER_SPELLING_ENGINE_H_