Home | History | Annotate | Download | only in spellchecker
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/spellchecker/spellcheck_service.h"
      6 
      7 #include "base/platform_file.h"
      8 #include "base/prefs/pref_member.h"
      9 #include "base/prefs/pref_service.h"
     10 #include "base/strings/string_split.h"
     11 #include "base/synchronization/waitable_event.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/spellchecker/spellcheck_factory.h"
     14 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
     15 #include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
     16 #include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
     17 #include "chrome/common/pref_names.h"
     18 #include "chrome/common/spellcheck_messages.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "content/public/browser/notification_service.h"
     21 #include "content/public/browser/notification_types.h"
     22 #include "content/public/browser/render_process_host.h"
     23 #include "ipc/ipc_platform_file.h"
     24 
     25 using content::BrowserThread;
     26 using chrome::spellcheck_common::WordList;
     27 
     28 // TODO(rlp): I do not like globals, but keeping these for now during
     29 // transition.
     30 // An event used by browser tests to receive status events from this class and
     31 // its derived classes.
     32 base::WaitableEvent* g_status_event = NULL;
     33 SpellcheckService::EventType g_status_type =
     34     SpellcheckService::BDICT_NOTINITIALIZED;
     35 
     36 SpellcheckService::SpellcheckService(Profile* profile)
     37     : profile_(profile),
     38       weak_ptr_factory_(this) {
     39   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     40   PrefService* prefs = profile_->GetPrefs();
     41   pref_change_registrar_.Init(prefs);
     42 
     43   std::string language_code;
     44   std::string country_code;
     45   chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
     46       prefs->GetString(prefs::kSpellCheckDictionary),
     47       &language_code,
     48       &country_code);
     49   feedback_sender_.reset(new spellcheck::FeedbackSender(
     50       profile->GetRequestContext(), language_code, country_code));
     51 
     52   pref_change_registrar_.Add(
     53       prefs::kEnableAutoSpellCorrect,
     54       base::Bind(&SpellcheckService::OnEnableAutoSpellCorrectChanged,
     55                  base::Unretained(this)));
     56   pref_change_registrar_.Add(
     57       prefs::kSpellCheckDictionary,
     58       base::Bind(&SpellcheckService::OnSpellCheckDictionaryChanged,
     59                  base::Unretained(this)));
     60  pref_change_registrar_.Add(
     61      prefs::kSpellCheckUseSpellingService,
     62      base::Bind(&SpellcheckService::OnUseSpellingServiceChanged,
     63                 base::Unretained(this)));
     64   pref_change_registrar_.Add(
     65       prefs::kEnableContinuousSpellcheck,
     66       base::Bind(&SpellcheckService::InitForAllRenderers,
     67                  base::Unretained(this)));
     68 
     69   OnSpellCheckDictionaryChanged();
     70 
     71   custom_dictionary_.reset(new SpellcheckCustomDictionary(profile_->GetPath()));
     72   custom_dictionary_->AddObserver(this);
     73   custom_dictionary_->Load();
     74 
     75   registrar_.Add(this,
     76                  content::NOTIFICATION_RENDERER_PROCESS_CREATED,
     77                  content::NotificationService::AllSources());
     78 }
     79 
     80 SpellcheckService::~SpellcheckService() {
     81   // Remove pref observers
     82   pref_change_registrar_.RemoveAll();
     83 }
     84 
     85 // static
     86 int SpellcheckService::GetSpellCheckLanguages(
     87     Profile* profile,
     88     std::vector<std::string>* languages) {
     89   StringPrefMember accept_languages_pref;
     90   StringPrefMember dictionary_language_pref;
     91   accept_languages_pref.Init(prefs::kAcceptLanguages, profile->GetPrefs());
     92   dictionary_language_pref.Init(prefs::kSpellCheckDictionary,
     93                                 profile->GetPrefs());
     94   std::string dictionary_language = dictionary_language_pref.GetValue();
     95 
     96   // Now scan through the list of accept languages, and find possible mappings
     97   // from this list to the existing list of spell check languages.
     98   std::vector<std::string> accept_languages;
     99 
    100 #if defined(OS_MACOSX)
    101   if (spellcheck_mac::SpellCheckerAvailable())
    102     spellcheck_mac::GetAvailableLanguages(&accept_languages);
    103   else
    104     base::SplitString(accept_languages_pref.GetValue(), ',', &accept_languages);
    105 #else
    106   base::SplitString(accept_languages_pref.GetValue(), ',', &accept_languages);
    107 #endif  // !OS_MACOSX
    108 
    109   GetSpellCheckLanguagesFromAcceptLanguages(
    110       accept_languages, dictionary_language, languages);
    111 
    112   for (size_t i = 0; i < languages->size(); ++i) {
    113     if ((*languages)[i] == dictionary_language)
    114       return i;
    115   }
    116   return -1;
    117 }
    118 
    119 // static
    120 void SpellcheckService::GetSpellCheckLanguagesFromAcceptLanguages(
    121     const std::vector<std::string>& accept_languages,
    122     const std::string& dictionary_language,
    123     std::vector<std::string>* languages) {
    124   // The current dictionary language should be there.
    125   languages->push_back(dictionary_language);
    126 
    127   for (std::vector<std::string>::const_iterator i = accept_languages.begin();
    128        i != accept_languages.end(); ++i) {
    129     std::string language =
    130         chrome::spellcheck_common::GetCorrespondingSpellCheckLanguage(*i);
    131     if (!language.empty() &&
    132         std::find(languages->begin(), languages->end(), language) ==
    133         languages->end()) {
    134       languages->push_back(language);
    135     }
    136   }
    137 }
    138 
    139 // static
    140 bool SpellcheckService::SignalStatusEvent(
    141     SpellcheckService::EventType status_type) {
    142   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    143 
    144   if (!g_status_event)
    145     return false;
    146   g_status_type = status_type;
    147   g_status_event->Signal();
    148   return true;
    149 }
    150 
    151 void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled) {
    152   metrics_.reset(new SpellCheckHostMetrics());
    153   metrics_->RecordEnabledStats(spellcheck_enabled);
    154   OnUseSpellingServiceChanged();
    155 }
    156 
    157 void SpellcheckService::InitForRenderer(content::RenderProcessHost* process) {
    158   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    159 
    160   Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
    161   if (SpellcheckServiceFactory::GetForProfile(profile) != this)
    162     return;
    163 
    164   PrefService* prefs = profile->GetPrefs();
    165   IPC::PlatformFileForTransit file = IPC::InvalidPlatformFileForTransit();
    166 
    167   if (hunspell_dictionary_->GetDictionaryFile() !=
    168       base::kInvalidPlatformFileValue) {
    169 #if defined(OS_POSIX)
    170     file = base::FileDescriptor(hunspell_dictionary_->GetDictionaryFile(),
    171                                 false);
    172 #elif defined(OS_WIN)
    173     BOOL ok = ::DuplicateHandle(::GetCurrentProcess(),
    174                                 hunspell_dictionary_->GetDictionaryFile(),
    175                                 process->GetHandle(),
    176                                 &file,
    177                                 0,
    178                                 false,
    179                                 DUPLICATE_SAME_ACCESS);
    180     DCHECK(ok) << ::GetLastError();
    181 #endif
    182   }
    183 
    184   process->Send(new SpellCheckMsg_Init(
    185       file,
    186       custom_dictionary_->GetWords(),
    187       hunspell_dictionary_->GetLanguage(),
    188       prefs->GetBoolean(prefs::kEnableAutoSpellCorrect)));
    189   process->Send(new SpellCheckMsg_EnableSpellCheck(
    190       prefs->GetBoolean(prefs::kEnableContinuousSpellcheck)));
    191 }
    192 
    193 SpellCheckHostMetrics* SpellcheckService::GetMetrics() const {
    194   return metrics_.get();
    195 }
    196 
    197 SpellcheckCustomDictionary* SpellcheckService::GetCustomDictionary() {
    198   return custom_dictionary_.get();
    199 }
    200 
    201 SpellcheckHunspellDictionary* SpellcheckService::GetHunspellDictionary() {
    202   return hunspell_dictionary_.get();
    203 }
    204 
    205 spellcheck::FeedbackSender* SpellcheckService::GetFeedbackSender() {
    206   return feedback_sender_.get();
    207 }
    208 
    209 bool SpellcheckService::LoadExternalDictionary(std::string language,
    210                                                std::string locale,
    211                                                std::string path,
    212                                                DictionaryFormat format) {
    213   return false;
    214 }
    215 
    216 bool SpellcheckService::UnloadExternalDictionary(std::string path) {
    217   return false;
    218 }
    219 
    220 void SpellcheckService::Observe(int type,
    221                                 const content::NotificationSource& source,
    222                                 const content::NotificationDetails& details) {
    223   DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CREATED);
    224   content::RenderProcessHost* process =
    225       content::Source<content::RenderProcessHost>(source).ptr();
    226   InitForRenderer(process);
    227 }
    228 
    229 void SpellcheckService::OnCustomDictionaryLoaded() {
    230   InitForAllRenderers();
    231 }
    232 
    233 void SpellcheckService::OnCustomDictionaryChanged(
    234     const SpellcheckCustomDictionary::Change& dictionary_change) {
    235   for (content::RenderProcessHost::iterator i(
    236           content::RenderProcessHost::AllHostsIterator());
    237        !i.IsAtEnd(); i.Advance()) {
    238     i.GetCurrentValue()->Send(new SpellCheckMsg_CustomDictionaryChanged(
    239         dictionary_change.to_add(),
    240         dictionary_change.to_remove()));
    241   }
    242 }
    243 
    244 void SpellcheckService::OnHunspellDictionaryInitialized() {
    245   InitForAllRenderers();
    246 }
    247 
    248 void SpellcheckService::OnHunspellDictionaryDownloadBegin() {
    249 }
    250 
    251 void SpellcheckService::OnHunspellDictionaryDownloadSuccess() {
    252 }
    253 
    254 void SpellcheckService::OnHunspellDictionaryDownloadFailure() {
    255 }
    256 
    257 // static
    258 void SpellcheckService::AttachStatusEvent(base::WaitableEvent* status_event) {
    259   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    260 
    261   g_status_event = status_event;
    262 }
    263 
    264 // static
    265 SpellcheckService::EventType SpellcheckService::GetStatusEvent() {
    266   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    267   return g_status_type;
    268 }
    269 
    270 void SpellcheckService::InitForAllRenderers() {
    271   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    272   for (content::RenderProcessHost::iterator i(
    273           content::RenderProcessHost::AllHostsIterator());
    274        !i.IsAtEnd(); i.Advance()) {
    275     content::RenderProcessHost* process = i.GetCurrentValue();
    276     if (process && process->GetHandle())
    277       InitForRenderer(process);
    278   }
    279 }
    280 
    281 void SpellcheckService::OnEnableAutoSpellCorrectChanged() {
    282   bool enabled = pref_change_registrar_.prefs()->GetBoolean(
    283       prefs::kEnableAutoSpellCorrect);
    284   for (content::RenderProcessHost::iterator i(
    285            content::RenderProcessHost::AllHostsIterator());
    286        !i.IsAtEnd(); i.Advance()) {
    287     content::RenderProcessHost* process = i.GetCurrentValue();
    288     process->Send(new SpellCheckMsg_EnableAutoSpellCorrect(enabled));
    289   }
    290 }
    291 
    292 void SpellcheckService::OnSpellCheckDictionaryChanged() {
    293   if (hunspell_dictionary_.get())
    294     hunspell_dictionary_->RemoveObserver(this);
    295   std::string dictionary =
    296       profile_->GetPrefs()->GetString(prefs::kSpellCheckDictionary);
    297   hunspell_dictionary_.reset(new SpellcheckHunspellDictionary(
    298       dictionary, profile_->GetRequestContext(), this));
    299   hunspell_dictionary_->AddObserver(this);
    300   hunspell_dictionary_->Load();
    301   std::string language_code;
    302   std::string country_code;
    303   chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
    304       dictionary, &language_code, &country_code);
    305   feedback_sender_->OnLanguageCountryChange(language_code, country_code);
    306 }
    307 
    308 void SpellcheckService::OnUseSpellingServiceChanged() {
    309   bool enabled = pref_change_registrar_.prefs()->GetBoolean(
    310       prefs::kSpellCheckUseSpellingService);
    311   if (metrics_)
    312     metrics_->RecordSpellingServiceStats(enabled);
    313 }
    314