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