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