diff options
author | rouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-20 08:26:46 +0000 |
---|---|---|
committer | rouslan@chromium.org <rouslan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-20 08:26:46 +0000 |
commit | 6cc4a844cc9caaf721728ba0097cff61bbf7fbeb (patch) | |
tree | e9cba89d5d28430f45e8053a274635006503386a | |
parent | 9687e5ef4025dca16d787afdef2bbda6c07e2272 (diff) | |
download | chromium_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
31 files changed, 684 insertions, 113 deletions
@@ -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_ |