summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorpinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-30 15:09:05 +0000
committerpinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-30 15:09:05 +0000
commit9b75b5b69a63c9a7167badc5ca33e9910d4e390a (patch)
treef4336ee158e22d709c07ba92d0e10f2ae6922836 /chrome
parentde9904623b5b91688a98ee987ee6ccfe2f584085 (diff)
downloadchromium_src-9b75b5b69a63c9a7167badc5ca33e9910d4e390a.zip
chromium_src-9b75b5b69a63c9a7167badc5ca33e9910d4e390a.tar.gz
chromium_src-9b75b5b69a63c9a7167badc5ca33e9910d4e390a.tar.bz2
Enable support for the Mac OS X spellchecking service in chromium.
spellchecker_platform_engine.h provides a basic interface for platform specific spellcheckers to follow. spellchecker_mac.mm implements these functions for the OS X spellchecking service. spellchecker_win.cc and spellchecker_linux.cc provide a space for future developments on these platforms. spellchecker_common.h contains a few shared variables and typedefs that are useful across all spellchecking code. spellchecker.cc has been modified to call the SpellCheckerPlatform::SpellCheckerAvailable() and use either hunspell or the platform spellchecker based on that call. Many new test cases have been added to one of the unit tests as well. chrome.gyp has been edited to reflect the added files. patch from pwicks86@gmail.com (paul wicks) BUG=13206 TEST=spellchecking works in web pages git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19585 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/spellcheck_unittest.cc299
-rw-r--r--chrome/browser/spellchecker.cc108
-rw-r--r--chrome/browser/spellchecker.h21
-rw-r--r--chrome/browser/spellchecker_common.h22
-rw-r--r--chrome/browser/spellchecker_linux.cc43
-rw-r--r--chrome/browser/spellchecker_mac.mm135
-rw-r--r--chrome/browser/spellchecker_platform_engine.h44
-rw-r--r--chrome/browser/spellchecker_win.cc43
-rw-r--r--chrome/chrome.gyp5
9 files changed, 684 insertions, 36 deletions
diff --git a/chrome/browser/spellcheck_unittest.cc b/chrome/browser/spellcheck_unittest.cc
index d3c3388..86eadff 100644
--- a/chrome/browser/spellcheck_unittest.cc
+++ b/chrome/browser/spellcheck_unittest.cc
@@ -306,6 +306,302 @@ TEST_F(SpellCheckTest, SpellCheckSuggestions_EN_US) {
// A suggested word that should occur.
const wchar_t* suggested_word;
} kTestCases[] = { // A valid English word with a preceding whitespace
+ // We need to have separate test cases here, since hunspell and the OS X
+ // spellchecking service occasionally differ on what they consider a valid
+ // suggestion for a given word, although these lists could likely be
+ // integrated somewhat.
+#if defined(OS_MACOSX)
+ // These words come from the wikipedia page of the most commonly
+ // misspelled words in english.
+ // (http://en.wikipedia.org/wiki/Commonly_misspelled_words).
+ {L"absense", false, 0,0,L"absence"},
+ {L"acceptible", false, 0,0,L"acceptable"},
+ {L"accidentaly", false, 0,0,L"accidentally"},
+ {L"accomodate", false, 0,0,L"accommodate"},
+ {L"acheive", false, 0,0,L"achieve"},
+ {L"acknowlege", false, 0,0,L"acknowledge"},
+ {L"acquaintence", false, 0,0,L"acquaintance"},
+ {L"aquire", false, 0,0,L"acquire"},
+ {L"aquit", false, 0,0,L"acquit"},
+ {L"acrage", false, 0,0,L"acreage"},
+ {L"adress", false, 0,0,L"address"},
+ {L"adultary", false, 0,0,L"adultery"},
+ {L"advertize", false, 0,0,L"advertise"},
+ {L"adviseable", false, 0,0,L"advisable"},
+ {L"agression", false, 0,0,L"aggression"},
+ {L"alchohol", false, 0,0,L"alcohol"},
+ {L"alege", false, 0,0,L"allege"},
+ {L"allegaince", false, 0,0,L"allegiance"},
+ {L"allmost", false, 0,0,L"almost"},
+ // Ideally, this test should pass. It works in firefox, but not in hunspell
+ // or OS X.
+ // {L"alot", false, 0,0,L"a lot"},
+ {L"amatuer", false, 0,0,L"amateur"},
+ {L"ammend", false, 0,0,L"amend"},
+ {L"amung", false, 0,0,L"among"},
+ {L"anually", false, 0,0,L"annually"},
+ {L"apparant", false, 0,0,L"apparent"},
+ {L"artic", false, 0,0,L"arctic"},
+ {L"arguement", false, 0,0,L"argument"},
+ {L"athiest", false, 0,0,L"atheist"},
+ {L"athelete", false, 0,0,L"athlete"},
+ {L"avrage", false, 0,0,L"average"},
+ {L"awfull", false, 0,0,L"awful"},
+ {L"ballance", false, 0,0,L"balance"},
+ {L"basicly", false, 0,0,L"basically"},
+ {L"becuase", false, 0,0,L"because"},
+ {L"becomeing", false, 0,0,L"becoming"},
+ {L"befor", false, 0,0,L"before"},
+ {L"begining", false, 0,0,L"beginning"},
+ {L"beleive", false, 0,0,L"believe"},
+ {L"bellweather", false, 0,0,L"bellwether"},
+ {L"benifit", false, 0,0,L"benefit"},
+ {L"bouy", false, 0,0,L"buoy"},
+ {L"briliant", false, 0,0,L"brilliant"},
+ {L"burgler", false, 0,0,L"burglar"},
+ {L"camoflage", false, 0,0,L"camouflage"},
+ {L"carrer", false, 0,0,L"career"},
+ {L"carefull", false, 0,0,L"careful"},
+ {L"Carribean", false, 0,0,L"Caribbean"},
+ {L"catagory", false, 0,0,L"category"},
+ {L"cauhgt", false, 0,0,L"caught"},
+ {L"cieling", false, 0,0,L"ceiling"},
+ {L"cemetary", false, 0,0,L"cemetery"},
+ {L"certin", false, 0,0,L"certain"},
+ {L"changable", false, 0,0,L"changeable"},
+ {L"cheif", false, 0,0,L"chief"},
+ {L"citezen", false, 0,0,L"citizen"},
+ {L"collaegue", false, 0,0,L"colleague"},
+ {L"colum", false, 0,0,L"column"},
+ {L"comming", false, 0,0,L"coming"},
+ {L"commited", false, 0,0,L"committed"},
+ {L"compitition", false, 0,0,L"competition"},
+ {L"conceed", false, 0,0,L"concede"},
+ {L"congradulate", false, 0,0,L"congratulate"},
+ // TODO(pwicks): This fails as a result of 13432.
+ // Once that is fixed, uncomment this.
+ // {L"consciencious", false, 0,0,L"conscientious"},
+ {L"concious", false, 0,0,L"conscious"},
+ {L"concensus", false, 0,0,L"consensus"},
+ {L"contraversy", false, 0,0,L"controversy"},
+ {L"conveniance", false, 0,0,L"convenience"},
+ {L"critecize", false, 0,0,L"criticize"},
+ {L"dacquiri", false, 0,0,L"daiquiri"},
+ {L"decieve", false, 0,0,L"deceive"},
+ {L"dicide", false, 0,0,L"decide"},
+ {L"definate", false, 0,0,L"definite"},
+ {L"definitly", false, 0,0,L"definitely"},
+ {L"deposite", false, 0,0,L"deposit"},
+ {L"desparate", false, 0,0,L"desperate"},
+ {L"develope", false, 0,0,L"develop"},
+ {L"diffrence", false, 0,0,L"difference"},
+ {L"dilema", false, 0,0,L"dilemma"},
+ {L"disapear", false, 0,0,L"disappear"},
+ {L"disapoint", false, 0,0,L"disappoint"},
+ {L"disasterous", false, 0,0,L"disastrous"},
+ {L"disipline", false, 0,0,L"discipline"},
+ {L"drunkeness", false, 0,0,L"drunkenness"},
+ {L"dumbell", false, 0,0,L"dumbbell"},
+ {L"durring", false, 0,0,L"during"},
+ {L"easely", false, 0,0,L"easily"},
+ {L"eigth", false, 0,0,L"eight"},
+ {L"embarass", false, 0,0,L"embarrass"},
+ {L"enviroment", false, 0,0,L"environment"},
+ {L"equiped", false, 0,0,L"equipped"},
+ {L"equiptment", false, 0,0,L"equipment"},
+ {L"exagerate", false, 0,0,L"exaggerate"},
+ {L"excede", false, 0,0,L"exceed"},
+ {L"exellent", false, 0,0,L"excellent"},
+ {L"exsept", false, 0,0,L"except"},
+ {L"exercize", false, 0,0,L"exercise"},
+ {L"exilerate", false, 0,0,L"exhilarate"},
+ {L"existance", false, 0,0,L"existence"},
+ {L"experiance", false, 0,0,L"experience"},
+ {L"experament", false, 0,0,L"experiment"},
+ {L"explaination", false, 0,0,L"explanation"},
+ {L"extreem", false, 0,0,L"extreme"},
+ {L"familier", false, 0,0,L"familiar"},
+ {L"facinating", false, 0,0,L"fascinating"},
+ {L"firey", false, 0,0,L"fiery"},
+ {L"finaly", false, 0,0,L"finally"},
+ {L"flourescent", false, 0,0,L"fluorescent"},
+ {L"foriegn", false, 0,0,L"foreign"},
+ {L"fourty", false, 0,0,L"forty"},
+ {L"foreward", false, 0,0,L"forward"},
+ {L"freind", false, 0,0,L"friend"},
+ {L"fullfil", false, 0,0,L"fulfill"},
+ {L"fundemental", false, 0,0,L"fundamental"},
+ {L"guage", false, 0,0,L"gauge"},
+ {L"generaly", false, 0,0,L"generally"},
+ {L"goverment", false, 0,0,L"government"},
+ {L"grammer", false, 0,0,L"grammar"},
+ {L"gratefull", false, 0,0,L"grateful"},
+ {L"garantee", false, 0,0,L"guarantee"},
+ {L"guidence", false, 0,0,L"guidance"},
+ {L"happyness", false, 0,0,L"happiness"},
+ {L"harrass", false, 0,0,L"harass"},
+ {L"heighth", false, 0,0,L"height"},
+ {L"heirarchy", false, 0,0,L"hierarchy"},
+ {L"humerous", false, 0,0,L"humorous"},
+ {L"hygene", false, 0,0,L"hygiene"},
+ {L"hipocrit", false, 0,0,L"hypocrite"},
+ {L"idenity", false, 0,0,L"identity"},
+ {L"ignorence", false, 0,0,L"ignorance"},
+ {L"imaginery", false, 0,0,L"imaginary"},
+ {L"immitate", false, 0,0,L"imitate"},
+ {L"immitation", false, 0,0,L"imitation"},
+ {L"imediately", false, 0,0,L"immediately"},
+ {L"incidently", false, 0,0,L"incidentally"},
+ {L"independant", false, 0,0,L"independent"},
+ // TODO(pwicks): This fails as a result of 13432.
+ // Once that is fixed, uncomment this.
+ // {L"indispensible", false, 0,0,L"indispensable"},
+ {L"innoculate", false, 0,0,L"inoculate"},
+ {L"inteligence", false, 0,0,L"intelligence"},
+ {L"intresting", false, 0,0,L"interesting"},
+ {L"interuption", false, 0,0,L"interruption"},
+ {L"irrelevent", false, 0,0,L"irrelevant"},
+ {L"irritible", false, 0,0,L"irritable"},
+ {L"iland", false, 0,0,L"island"},
+ {L"jellous", false, 0,0,L"jealous"},
+ {L"knowlege", false, 0,0,L"knowledge"},
+ {L"labratory", false, 0,0,L"laboratory"},
+ {L"liesure", false, 0,0,L"leisure"},
+ {L"lenght", false, 0,0,L"length"},
+ {L"liason", false, 0,0,L"liaison"},
+ {L"libary", false, 0,0,L"library"},
+ {L"lisence", false, 0,0,L"license"},
+ {L"lonelyness", false, 0,0,L"loneliness"},
+ {L"lieing", false, 0,0,L"lying"},
+ {L"maintenence", false, 0,0,L"maintenance"},
+ {L"manuever", false, 0,0,L"maneuver"},
+ {L"marrige", false, 0,0,L"marriage"},
+ {L"mathmatics", false, 0,0,L"mathematics"},
+ {L"medcine", false, 0,0,L"medicine"},
+ {L"medeval", false, 0,0,L"medieval"},
+ {L"momento", false, 0,0,L"memento"},
+ {L"millenium", false, 0,0,L"millennium"},
+ {L"miniture", false, 0,0,L"miniature"},
+ {L"minite", false, 0,0,L"minute"},
+ {L"mischevous", false, 0,0,L"mischievous"},
+ {L"mispell", false, 0,0,L"misspell"},
+ // Maybe this one should pass, as it works in hunspell, but not in firefox.
+ // {L"misterius", false, 0,0,L"mysterious"},
+ {L"naturaly", false, 0,0,L"naturally"},
+ {L"neccessary", false, 0,0,L"necessary"},
+ {L"neice", false, 0,0,L"niece"},
+ {L"nieghbor", false, 0,0,L"neighbor"},
+ {L"nieghbour", false, 0,0,L"neighbor"},
+ {L"niether", false, 0,0,L"neither"},
+ {L"noticable", false, 0,0,L"noticeable"},
+ {L"occassion", false, 0,0,L"occasion"},
+ {L"occasionaly", false, 0,0,L"occasionally"},
+ {L"occurrance", false, 0,0,L"occurrence"},
+ {L"occured", false, 0,0,L"occurred"},
+ {L"oficial", false, 0,0,L"official"},
+ {L"offen", false, 0,0,L"often"},
+ {L"ommision", false, 0,0,L"omission"},
+ {L"oprate", false, 0,0,L"operate"},
+ {L"oppurtunity", false, 0,0,L"opportunity"},
+ {L"orignal", false, 0,0,L"original"},
+ {L"outragous", false, 0,0,L"outrageous"},
+ {L"parrallel", false, 0,0,L"parallel"},
+ {L"parliment", false, 0,0,L"parliament"},
+ {L"particurly", false, 0,0,L"particularly"},
+ {L"passtime", false, 0,0,L"pastime"},
+ {L"peculier", false, 0,0,L"peculiar"},
+ {L"percieve", false, 0,0,L"perceive"},
+ {L"pernament", false, 0,0,L"permanent"},
+ {L"perseverence", false, 0,0,L"perseverance"},
+ {L"personaly", false, 0,0,L"personally"},
+ {L"personell", false, 0,0,L"personnel"},
+ {L"persaude", false, 0,0,L"persuade"},
+ {L"pichure", false, 0,0,L"picture"},
+ {L"peice", false, 0,0,L"piece"},
+ {L"plagerize", false, 0,0,L"plagiarize"},
+ {L"playright", false, 0,0,L"playwright"},
+ {L"plesant", false, 0,0,L"pleasant"},
+ {L"pollitical", false, 0,0,L"political"},
+ {L"posession", false, 0,0,L"possession"},
+ {L"potatos", false, 0,0,L"potatoes"},
+ {L"practicle", false, 0,0,L"practical"},
+ {L"preceed", false, 0,0,L"precede"},
+ {L"predjudice", false, 0,0,L"prejudice"},
+ {L"presance", false, 0,0,L"presence"},
+ {L"privelege", false, 0,0,L"privilege"},
+ // This one should probably work. It does in FF and Hunspell.
+ // {L"probly", false, 0,0,L"probably"},
+ {L"proffesional", false, 0,0,L"professional"},
+ {L"professer", false, 0,0,L"professor"},
+ {L"promiss", false, 0,0,L"promise"},
+ // TODO(pwicks): This fails as a result of 13432.
+ // Once that is fixed, uncomment this.
+ // {L"pronounciation", false, 0,0,L"pronunciation"},
+ {L"prufe", false, 0,0,L"proof"},
+ {L"psycology", false, 0,0,L"psychology"},
+ {L"publically", false, 0,0,L"publicly"},
+ {L"quanity", false, 0,0,L"quantity"},
+ {L"quarentine", false, 0,0,L"quarantine"},
+ {L"questionaire", false, 0,0,L"questionnaire"},
+ {L"readible", false, 0,0,L"readable"},
+ {L"realy", false, 0,0,L"really"},
+ {L"recieve", false, 0,0,L"receive"},
+ {L"reciept", false, 0,0,L"receipt"},
+ {L"reconize", false, 0,0,L"recognize"},
+ {L"recomend", false, 0,0,L"recommend"},
+ {L"refered", false, 0,0,L"referred"},
+ {L"referance", false, 0,0,L"reference"},
+ {L"relevent", false, 0,0,L"relevant"},
+ {L"religous", false, 0,0,L"religious"},
+ {L"repitition", false, 0,0,L"repetition"},
+ {L"restarant", false, 0,0,L"restaurant"},
+ {L"rythm", false, 0,0,L"rhythm"},
+ {L"rediculous", false, 0,0,L"ridiculous"},
+ {L"sacrefice", false, 0,0,L"sacrifice"},
+ {L"saftey", false, 0,0,L"safety"},
+ {L"sissors", false, 0,0,L"scissors"},
+ {L"secratary", false, 0,0,L"secretary"},
+ {L"sieze", false, 0,0,L"seize"},
+ {L"seperate", false, 0,0,L"separate"},
+ {L"sargent", false, 0,0,L"sergeant"},
+ {L"shineing", false, 0,0,L"shining"},
+ {L"similer", false, 0,0,L"similar"},
+ {L"sinceerly", false, 0,0,L"sincerely"},
+ {L"speach", false, 0,0,L"speech"},
+ {L"stoping", false, 0,0,L"stopping"},
+ {L"strenght", false, 0,0,L"strength"},
+ {L"succede", false, 0,0,L"succeed"},
+ {L"succesful", false, 0,0,L"successful"},
+ {L"supercede", false, 0,0,L"supersede"},
+ {L"surelly", false, 0,0,L"surely"},
+ {L"suprise", false, 0,0,L"surprise"},
+ {L"temperture", false, 0,0,L"temperature"},
+ {L"temprary", false, 0,0,L"temporary"},
+ {L"tomatos", false, 0,0,L"tomatoes"},
+ {L"tommorrow", false, 0,0,L"tomorrow"},
+ {L"tounge", false, 0,0,L"tongue"},
+ {L"truely", false, 0,0,L"truly"},
+ {L"twelth", false, 0,0,L"twelfth"},
+ {L"tyrany", false, 0,0,L"tyranny"},
+ {L"underate", false, 0,0,L"underrate"},
+ {L"untill", false, 0,0,L"until"},
+ {L"unuseual", false, 0,0,L"unusual"},
+ {L"upholstry", false, 0,0,L"upholstery"},
+ {L"usible", false, 0,0,L"usable"},
+ {L"useing", false, 0,0,L"using"},
+ {L"usualy", false, 0,0,L"usually"},
+ {L"vaccuum", false, 0,0,L"vacuum"},
+ {L"vegatarian", false, 0,0,L"vegetarian"},
+ {L"vehical", false, 0,0,L"vehicle"},
+ {L"visious", false, 0,0,L"vicious"},
+ {L"villege", false, 0,0,L"village"},
+ {L"wierd", false, 0,0,L"weird"},
+ {L"wellcome", false, 0,0,L"welcome"},
+ {L"wellfare", false, 0,0,L"welfare"},
+ {L"wilfull", false, 0,0,L"willful"},
+ {L"withold", false, 0,0,L"withhold"},
+ {L"writting", false, 0,0,L"writing"},
+#else
{L"ello", false, 0, 0, L"hello"},
{L"ello", false, 0, 0, L"cello"},
{L"wate", false, 0, 0, L"water"},
@@ -316,6 +612,7 @@ TEST_F(SpellCheckTest, SpellCheckSuggestions_EN_US) {
{L"jum", false, 0, 0, L"rum"},
{L"jum", false, 0, 0, L"sum"},
{L"jum", false, 0, 0, L"tum"},
+#endif //!OS_MACOSX
// TODO (Sidchat): add many more examples.
};
@@ -360,7 +657,7 @@ TEST_F(SpellCheckTest, DISABLED_SpellCheckAddToDictionary_EN_US) {
static const struct {
// A string to be added to SpellChecker.
const wchar_t* word_to_add;
- } kTestCases[] = { // word to be added to SpellChecker
+ } kTestCases[] = { // Words to be added to the SpellChecker.
{L"Googley"},
{L"Googleplex"},
{L"Googler"},
diff --git a/chrome/browser/spellchecker.cc b/chrome/browser/spellchecker.cc
index 2a8053d..ec8aa38 100644
--- a/chrome/browser/spellchecker.cc
+++ b/chrome/browser/spellchecker.cc
@@ -1,9 +1,11 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2009 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 "app/l10n_util.h"
#include "chrome/browser/spellchecker.h"
+#include "chrome/browser/spellchecker_common.h"
+#include "chrome/browser/spellchecker_platform_engine.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
@@ -28,9 +30,7 @@
using base::TimeTicks;
-static const int kMaxSuggestions = 5; // Max number of dictionary suggestions.
-static const int kMaxAutoCorrectWordSize = 8;
namespace {
@@ -393,8 +393,19 @@ SpellChecker::SpellChecker(const FilePath& dict_dir,
url_request_context_(request_context),
dic_is_downloading_(false),
auto_spell_correct_turned_on_(false),
+ is_using_platform_spelling_engine_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(
dic_download_state_changer_factory_(this)) {
+ if (SpellCheckerPlatform::SpellCheckerAvailable()) {
+ SpellCheckerPlatform::Init();
+ if (SpellCheckerPlatform::PlatformSupportsLanguage(language)) {
+ // If we have reached here, then we know that the current platform
+ // supports the given language and we will use it instead of hunspell.
+ SpellCheckerPlatform::SetLanguage(language);
+ is_using_platform_spelling_engine_ = true;
+ }
+ }
+
// Remember UI loop to later use this as a proxy to get IO loop.
ui_loop_ = MessageLoop::current();
@@ -557,7 +568,7 @@ bool SpellChecker::IsValidContraction(const string16& contraction) {
int word_start;
int word_length;
while (word_iterator.GetNextWord(&word, &word_start, &word_length)) {
- if (!hunspell_->spell(UTF16ToUTF8(word).c_str()))
+ if (!CheckSpelling(UTF16ToUTF8(word)))
return false;
}
return true;
@@ -580,17 +591,22 @@ bool SpellChecker::SpellCheckWord(
worker_loop_ = MessageLoop::current();
#endif
- Initialize();
+ // Check if the platform spellchecker is being used.
+ if (!is_using_platform_spelling_engine_) {
+ // If it isn't, try and init hunspell.
+ Initialize();
+
+ // Check to see if hunspell was successful.
+ if (!hunspell_.get())
+ return true; // Unable to spellcheck, return word is OK.
+ }
StatsScope<StatsRate> timer(chrome::Counters::spellcheck_lookup());
*misspelling_start = 0;
*misspelling_len = 0;
if (in_word_len == 0)
- return true; // no input means always spelled correctly
-
- if (!hunspell_.get())
- return true; // unable to spellcheck, return word is OK
+ return true; // No input means always spelled correctly.
SpellcheckWordIterator word_iterator;
string16 word;
@@ -601,16 +617,12 @@ bool SpellChecker::SpellCheckWord(
word_iterator.Initialize(&character_attributes_, in_word_utf16.c_str(),
in_word_len, true);
while (word_iterator.GetNextWord(&word, &word_start, &word_length)) {
- // Found a word (or a contraction) that hunspell can check its spelling.
+ // Found a word (or a contraction) that the spellchecker can check the
+ // spelling of.
std::string encoded_word = UTF16ToUTF8(word);
-
- {
- TimeTicks begin_time = TimeTicks::Now();
- bool word_ok = !!hunspell_->spell(encoded_word.c_str());
- DHISTOGRAM_TIMES("Spellcheck.CheckTime", TimeTicks::Now() - begin_time);
- if (word_ok)
- continue;
- }
+ bool word_ok = CheckSpelling(encoded_word);
+ if (word_ok)
+ continue;
// If the given word is a concatenated word of two or more valid words
// (e.g. "hello:hello"), we should treat it as a valid word.
@@ -622,20 +634,7 @@ bool SpellChecker::SpellCheckWord(
// Get the list of suggested words.
if (optional_suggestions) {
- char** suggestions;
- TimeTicks begin_time = TimeTicks::Now();
- int number_of_suggestions = hunspell_->suggest(&suggestions,
- encoded_word.c_str());
- DHISTOGRAM_TIMES("Spellcheck.SuggestTime",
- TimeTicks::Now() - begin_time);
-
- // Populate the vector of WideStrings.
- for (int i = 0; i < number_of_suggestions; i++) {
- if (i < kMaxSuggestions)
- optional_suggestions->push_back(UTF8ToWide(suggestions[i]));
- free(suggestions[i]);
- }
- free(suggestions);
+ FillSuggestionList(encoded_word, optional_suggestions);
}
return false;
}
@@ -671,6 +670,11 @@ class AddWordToCustomDictionaryTask : public Task {
};
void SpellChecker::AddWord(const std::wstring& word) {
+ if (is_using_platform_spelling_engine_) {
+ SpellCheckerPlatform::AddWord(word);
+ return;
+ }
+
// Check if the |hunspell_| has been initialized at all.
Initialize();
@@ -687,3 +691,43 @@ void SpellChecker::AddWord(const std::wstring& word) {
else
write_word_task->Run();
}
+
+bool SpellChecker::CheckSpelling(const std::string& word_to_check) {
+ bool word_correct = false;
+
+ TimeTicks begin_time = TimeTicks::Now();
+ if (is_using_platform_spelling_engine_) {
+ word_correct = SpellCheckerPlatform::CheckSpelling(word_to_check);
+ } else {
+ // |hunspell_->spell| returns 0 if the word is spelled correctly and
+ // non-zero otherwsie.
+ word_correct = (hunspell_->spell(word_to_check.c_str()) != 0);
+ }
+ DHISTOGRAM_TIMES("Spellcheck.CheckTime", TimeTicks::Now() - begin_time);
+
+ return word_correct;
+}
+
+
+void SpellChecker::FillSuggestionList(const std::string& wrong_word,
+ std::vector<std::wstring>* optional_suggestions) {
+ if (is_using_platform_spelling_engine_) {
+ SpellCheckerPlatform::FillSuggestionList(wrong_word, optional_suggestions);
+ return;
+ }
+ char** suggestions;
+ TimeTicks begin_time = TimeTicks::Now();
+ int number_of_suggestions = hunspell_->suggest(&suggestions,
+ wrong_word.c_str());
+ DHISTOGRAM_TIMES("Spellcheck.SuggestTime",
+ TimeTicks::Now() - begin_time);
+
+ // Populate the vector of WideStrings.
+ for (int i = 0; i < number_of_suggestions; i++) {
+ if (i < kMaxSuggestions)
+ optional_suggestions->push_back(UTF8ToWide(suggestions[i]));
+ free(suggestions[i]);
+ }
+ if (suggestions != NULL)
+ free(suggestions);
+}
diff --git a/chrome/browser/spellchecker.h b/chrome/browser/spellchecker.h
index a9c2946..40ba3f3 100644
--- a/chrome/browser/spellchecker.h
+++ b/chrome/browser/spellchecker.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2009 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.
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_SPELLCHECKER_H_
#include <vector>
+#include <string>
#include "app/l10n_util.h"
#include "base/string_util.h"
@@ -112,6 +113,16 @@ class SpellChecker : public base::RefCountedThreadSafe<SpellChecker> {
static Language GetLanguageFromLanguageRegion(Language input_language);
private:
+
+ // When called, relays the request to check the spelling to the proper
+ // backend, either hunspell or a platform-specific backend.
+ bool CheckSpelling(const std::string& word_to_check);
+
+ // When called, relays the request to fill the list with suggestions to
+ // the proper backend, either hunspell or a platform-specific backend.
+ void FillSuggestionList(const std::string& wrong_word,
+ std::vector<std::wstring>* optional_suggestions);
+
// Download dictionary files when required.
class DictionaryDownloadController;
@@ -119,7 +130,7 @@ class SpellChecker : public base::RefCountedThreadSafe<SpellChecker> {
bool Initialize();
// After |hunspell_| is initialized, this function is called to add custom
- // words from the custom dictionary to the |hunspell_|
+ // words from the custom dictionary to the |hunspell_|.
void AddCustomWordsToHunspell();
void set_file_is_downloading(bool value);
@@ -191,6 +202,10 @@ class SpellChecker : public base::RefCountedThreadSafe<SpellChecker> {
// Remember state for auto spell correct.
bool auto_spell_correct_turned_on_;
+ // True if a platform-specific spellchecking engine is being used,
+ // and False if hunspell is being used.
+ bool is_using_platform_spelling_engine_;
+
// Used for generating callbacks to spellchecker, since spellchecker is a
// non-reference counted object. The callback is generated by generating tasks
// using NewRunableMethod on these objects.
@@ -199,4 +214,4 @@ class SpellChecker : public base::RefCountedThreadSafe<SpellChecker> {
DISALLOW_COPY_AND_ASSIGN(SpellChecker);
};
-#endif // #ifndef CHROME_BROWSER_SPELLCHECKER_H_
+#endif // CHROME_BROWSER_SPELLCHECKER_H_
diff --git a/chrome/browser/spellchecker_common.h b/chrome/browser/spellchecker_common.h
new file mode 100644
index 0000000..a64b724
--- /dev/null
+++ b/chrome/browser/spellchecker_common.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2009 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_SPELLCHECKER_COMMON_H_
+#define CHROME_BROWSER_SPELLCHECKER_COMMON_H_
+
+#include <string>
+#include <vector>
+
+// Some constants and typedefs that are common to all spellchecker
+// files/classes/backends/platforms/whatever.
+
+typedef std::string Language;
+typedef std::vector<Language> Languages;
+
+static const int kMaxSuggestions = 5; // Max number of dictionary suggestions.
+
+static const int kMaxAutoCorrectWordSize = 8;
+
+#endif // CHROME_BROWSER_SPELLCHECKER_COMMON_H_
+
diff --git a/chrome/browser/spellchecker_linux.cc b/chrome/browser/spellchecker_linux.cc
new file mode 100644
index 0000000..25fcbff
--- /dev/null
+++ b/chrome/browser/spellchecker_linux.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2009 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.
+
+// If linux ever gains a platform specific spellchecker, it will be
+// implemented here.
+
+#include "chrome/browser/spellchecker_common.h"
+
+namespace SpellCheckerPlatform {
+
+bool SpellCheckerAvailable() {
+ // As of Summer 2009, there is no commonly accepted platform spellchecker
+ // for linux, so we'll return false here.
+ return false;
+}
+
+// The following methods are just stubs to keep the linker happy.
+bool PlatformSupportsLanguage(const Language& current_language) {
+ return false;
+}
+
+void Init() {
+}
+
+void SetLanguage(const Language& lang_to_set) {
+}
+
+bool CheckSpelling(const std::string& word_to_check) {
+ return false;
+}
+
+void FillSuggestionList(const std::string& wrong_word,
+ std::vector<std::wstring>* optional_suggestions) {
+}
+
+void AddWord(const std::wstring& word) {
+}
+
+void RemoveWord(const std::wstring& word) {
+}
+
+} // namespace SpellCheckerPlatform
diff --git a/chrome/browser/spellchecker_mac.mm b/chrome/browser/spellchecker_mac.mm
new file mode 100644
index 0000000..81aa9e3
--- /dev/null
+++ b/chrome/browser/spellchecker_mac.mm
@@ -0,0 +1,135 @@
+// Copyright (c) 2006-2009 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.
+
+// This file implements the interface defined in spellchecker_platform_engine.h
+// for the OS X platform.
+
+#import <Cocoa/Cocoa.h>
+
+#include "chrome/browser/spellchecker_common.h"
+#include "chrome/browser/spellchecker_platform_engine.h"
+#include "base/time.h"
+#include "base/histogram.h"
+#include "base/sys_string_conversions.h"
+
+using base::TimeTicks;
+namespace {
+// The number of characters in the first part of the language code.
+const unsigned int kShortLanguageCodeSize = 2;
+
+// A private utility function to convert hunspell language codes to os x
+// language codes.
+NSString* ConvertLanguageCodeToMac(const Language& lang_code) {
+ NSString* whole_code = base::SysUTF8ToNSString(lang_code);
+
+ if ([whole_code length] > kShortLanguageCodeSize) {
+ NSString* lang_code = [whole_code
+ substringToIndex:kShortLanguageCodeSize];
+ // Add 1 here to skip the underscore.
+ NSString* region_code = [whole_code
+ substringFromIndex:(kShortLanguageCodeSize + 1)];
+
+ // Check for the special case of en-US and pt-PT, since os x lists these
+ // as just en and pt respectively.
+ // TODO(pwicks): Find out if there are other special cases for languages
+ // not installed on the system by default. Are there others like pt-PT?
+ if (([lang_code isEqualToString:@"en"] &&
+ [region_code isEqualToString:@"US"]) ||
+ ([lang_code isEqualToString:@"pt"] &&
+ [region_code isEqualToString:@"PT"])) {
+ return lang_code;
+ }
+
+ // Otherwise, just build a string that uses an underscore instead of a
+ // dash between the language and the region code, since this is the
+ // format that os x uses.
+ NSString* os_x_language =
+ [NSString stringWithFormat:@"%@_%@", lang_code, region_code];
+ return os_x_language;
+ } else {
+ // This is just a language code with the same format as os x
+ // language code.
+ return whole_code;
+ }
+}
+} // namespace
+
+namespace SpellCheckerPlatform {
+
+bool SpellCheckerAvailable() {
+ // If this file was compiled, then we know that we are on OS X 10.5 at least
+ // and can safely return true here.
+ return true;
+}
+
+void Init() {
+ // This call must be made before the call to
+ // [NSSpellchecker sharedSpellChecker] or it will return nil.
+ [NSApplication sharedApplication];
+}
+
+bool PlatformSupportsLanguage(const Language& current_language) {
+ // First, convert Language to an osx lang code NSString.
+ NSString* mac_lang_code = ConvertLanguageCodeToMac(current_language);
+
+ // Then grab the languages available.
+ NSArray* availableLanguages;
+ availableLanguages = [[NSSpellChecker sharedSpellChecker]
+ availableLanguages];
+
+ // Return true if the given languange is supported by os x.
+ return [availableLanguages containsObject:mac_lang_code];
+}
+
+void SetLanguage(const Language& lang_to_set) {
+ NSString* NS_lang_to_set = ConvertLanguageCodeToMac(lang_to_set);
+ [[NSSpellChecker sharedSpellChecker] setLanguage:NS_lang_to_set];
+}
+
+bool CheckSpelling(const std::string& word_to_check) {
+ // [[NSSpellChecker sharedSpellChecker] checkSpellingOfString] returns an
+ // NSRange that we can look at to determine if a word is misspelled.
+ NSRange spell_range = {0,0};
+
+ // Convert the word to an NSString.
+ NSString* NS_word_to_check = base::SysUTF8ToNSString(word_to_check);
+ // Check the spelling, starting at the beginning of the word.
+ spell_range = [[NSSpellChecker sharedSpellChecker]
+ checkSpellingOfString:NS_word_to_check startingAt:0];
+
+ // If the length of the misspelled word == 0,
+ // then there is no misspelled word.
+ bool word_correct = (spell_range.length == 0);
+ return word_correct;
+}
+
+void FillSuggestionList(const std::string& wrong_word,
+ std::vector<std::wstring>* optional_suggestions) {
+ NSString* NS_wrong_word = base::SysUTF8ToNSString(wrong_word);
+ TimeTicks begin_time = TimeTicks::Now();
+ // The suggested words for |wrong_word|.
+ NSArray* guesses =
+ [[NSSpellChecker sharedSpellChecker] guessesForWord:NS_wrong_word];
+ DHISTOGRAM_TIMES("Spellcheck.SuggestTime",
+ TimeTicks::Now() - begin_time);
+
+ for (int i = 0; i < static_cast<int>([guesses count]); i++) {
+ if (i < kMaxSuggestions) {
+ optional_suggestions->push_back(base::SysNSStringToWide(
+ [guesses objectAtIndex:i]));
+ }
+ }
+}
+
+void AddWord(const std::wstring& word) {
+ NSString* word_to_add = base::SysWideToNSString(word);
+ [[NSSpellChecker sharedSpellChecker] learnWord:word_to_add];
+}
+
+void RemoveWord(const std::wstring& word) {
+ NSString *word_to_remove = base::SysWideToNSString(word);
+ [[NSSpellChecker sharedSpellChecker] unlearnWord:word_to_remove];
+}
+} // namespace SpellCheckerPlatform
+
diff --git a/chrome/browser/spellchecker_platform_engine.h b/chrome/browser/spellchecker_platform_engine.h
new file mode 100644
index 0000000..74a409a
--- /dev/null
+++ b/chrome/browser/spellchecker_platform_engine.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2009 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.
+
+// This file defines the interface that any platform-specific spellchecker
+// needs to implement in order to be used by the browser.
+
+#ifndef CHROME_BROWSER_SPELLCHECKER_PLATFORM_ENGINE_H_
+#define CHROME_BROWSER_SPELLCHECKER_PLATFORM_ENGINE_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/spellchecker_common.h"
+
+namespace SpellCheckerPlatform {
+// Returns true if there is an platform-specific spellchecker.
+bool SpellCheckerAvailable();
+// Do any initialization needed for spellchecker.
+void Init();
+// TODO(pwicks): should we add a companion to this, TearDown or something?
+
+// Translates the codes used by chrome to the language codes used by os x
+// and checks the given language agains the languages that the current system
+// supports. If the platform-specific spellchecker supports the language,
+// then returns true, otherwise false.
+bool PlatformSupportsLanguage(const Language& current_language);
+// Sets the language for the platform-specific spellchecker.
+void SetLanguage(const Language& lang_to_set);
+// Checks the spelling of the given string, using the platform-specific
+// spellchecker. Returns true if the word is spelled correctly.
+bool CheckSpelling(const std::string& word_to_check);
+// Fills the given vector |optional_suggestions| with a number (up to
+// kMaxSuggestions, which is defined in spellchecker_common.h) of suggestions
+// for the string |wrong_word|.
+void FillSuggestionList(const std::string& wrong_word,
+ std::vector<std::wstring>* optional_suggestions);
+// Adds the given word to the platform dictionary.
+void AddWord(const std::wstring& word);
+// Remove a given word from the platform dictionary.
+void RemoveWord(const std::wstring& word);
+}
+
+#endif // CHROME_BROWSER_SPELLCHECKER_PLATFORM_ENGINE_H_
diff --git a/chrome/browser/spellchecker_win.cc b/chrome/browser/spellchecker_win.cc
new file mode 100644
index 0000000..2632ee6
--- /dev/null
+++ b/chrome/browser/spellchecker_win.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2009 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.
+
+// If windows ever gains a platform specific spellchecker, it will be
+// implemented here.
+
+#include "chrome/browser/spellchecker_common.h"
+
+namespace SpellCheckerPlatform {
+
+bool SpellCheckerAvailable() {
+ // No current version of Windows (as of Summer 2009) has a common spellchecker
+ // so we'll return false here.
+ return false;
+}
+
+// The following methods are just stubs to keep the linker happy.
+bool PlatformSupportsLanguage(const Language& current_language) {
+ return false;
+}
+
+void Init() {
+}
+
+void SetLanguage(const Language& lang_to_set) {
+}
+
+bool CheckSpelling(const std::string& word_to_check) {
+ return false;
+}
+
+void FillSuggestionList(const std::string& wrong_word,
+ std::vector<std::wstring>* optional_suggestions) {
+}
+
+void AddWord(const std::wstring& word) {
+}
+
+void RemoveWord(const std::wstring& word) {
+}
+
+} // namespace SpellCheckerPlatform
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 5f17dbe..c5011db 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1405,6 +1405,11 @@
'browser/spellcheck_worditerator.h',
'browser/spellchecker.cc',
'browser/spellchecker.h',
+ 'browser/spellchecker_common.h',
+ 'browser/spellchecker_linux.cc',
+ 'browser/spellchecker_mac.mm',
+ 'browser/spellchecker_platform_engine.h',
+ 'browser/spellchecker_win.cc',
'browser/ssl/ssl_blocking_page.cc',
'browser/ssl/ssl_blocking_page.h',
'browser/ssl/ssl_cert_error_handler.cc',