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