Popular sites on the NTP: re-download popular suggestions once per Chrome run
[chromium-blink-merge.git] / chrome / browser / spellchecker / spellcheck_service.cc
blob1cebb74f35279febbd3c1fbbfed1410acffea1df
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.
5 #include "chrome/browser/spellchecker/spellcheck_service.h"
7 #include "base/logging.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/supports_user_data.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "chrome/browser/spellchecker/feedback_sender.h"
14 #include "chrome/browser/spellchecker/spellcheck_factory.h"
15 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
16 #include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
17 #include "chrome/browser/spellchecker/spellcheck_platform.h"
18 #include "chrome/browser/spellchecker/spelling_service_client.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/common/spellcheck_bdict_language.h"
21 #include "chrome/common/spellcheck_common.h"
22 #include "chrome/common/spellcheck_messages.h"
23 #include "components/user_prefs/user_prefs.h"
24 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "ipc/ipc_platform_file.h"
31 using content::BrowserThread;
33 // TODO(rlp): I do not like globals, but keeping these for now during
34 // transition.
35 // An event used by browser tests to receive status events from this class and
36 // its derived classes.
37 base::WaitableEvent* g_status_event = NULL;
38 SpellcheckService::EventType g_status_type =
39 SpellcheckService::BDICT_NOTINITIALIZED;
41 SpellcheckService::SpellcheckService(content::BrowserContext* context)
42 : context_(context),
43 weak_ptr_factory_(this) {
44 DCHECK_CURRENTLY_ON(BrowserThread::UI);
45 PrefService* prefs = user_prefs::UserPrefs::Get(context);
46 pref_change_registrar_.Init(prefs);
47 StringListPrefMember dictionaries_pref;
48 dictionaries_pref.Init(prefs::kSpellCheckDictionaries, prefs);
49 std::string first_of_dictionaries;
50 if (!dictionaries_pref.GetValue().empty())
51 first_of_dictionaries = dictionaries_pref.GetValue().front();
53 // For preference migration, set the new preference kSpellCheckDictionaries
54 // to be the same as the old kSpellCheckDictionary.
55 StringPrefMember single_dictionary_pref;
56 single_dictionary_pref.Init(prefs::kSpellCheckDictionary, prefs);
57 std::string single_dictionary = single_dictionary_pref.GetValue();
59 if (first_of_dictionaries.empty() && !single_dictionary.empty()) {
60 first_of_dictionaries = single_dictionary;
61 dictionaries_pref.SetValue(
62 std::vector<std::string>(1, first_of_dictionaries));
65 single_dictionary_pref.SetValue("");
67 // If a user goes from single language to multi-language spellchecking with
68 // spellchecking disabled the dictionaries preference should be blanked.
69 if (!prefs->GetBoolean(prefs::kEnableContinuousSpellcheck) &&
70 chrome::spellcheck_common::IsMultilingualSpellcheckEnabled()) {
71 dictionaries_pref.SetValue(std::vector<std::string>());
72 prefs->SetBoolean(prefs::kEnableContinuousSpellcheck, true);
75 // If a user goes back to single language spellchecking make sure there is
76 // only one language in the dictionaries preference.
77 if (!chrome::spellcheck_common::IsMultilingualSpellcheckEnabled() &&
78 dictionaries_pref.GetValue().size() > 1) {
79 dictionaries_pref.SetValue(
80 std::vector<std::string>(1, first_of_dictionaries));
83 std::string language_code;
84 std::string country_code;
85 chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
86 first_of_dictionaries,
87 &language_code,
88 &country_code);
89 feedback_sender_.reset(new spellcheck::FeedbackSender(
90 context->GetRequestContext(), language_code, country_code));
92 pref_change_registrar_.Add(
93 prefs::kEnableAutoSpellCorrect,
94 base::Bind(&SpellcheckService::OnEnableAutoSpellCorrectChanged,
95 base::Unretained(this)));
96 pref_change_registrar_.Add(
97 prefs::kSpellCheckDictionaries,
98 base::Bind(&SpellcheckService::OnSpellCheckDictionariesChanged,
99 base::Unretained(this)));
100 pref_change_registrar_.Add(
101 prefs::kSpellCheckUseSpellingService,
102 base::Bind(&SpellcheckService::OnUseSpellingServiceChanged,
103 base::Unretained(this)));
105 pref_change_registrar_.Add(
106 prefs::kEnableContinuousSpellcheck,
107 base::Bind(&SpellcheckService::InitForAllRenderers,
108 base::Unretained(this)));
110 OnSpellCheckDictionariesChanged();
112 custom_dictionary_.reset(new SpellcheckCustomDictionary(context_->GetPath()));
113 custom_dictionary_->AddObserver(this);
114 custom_dictionary_->Load();
116 registrar_.Add(this,
117 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
118 content::NotificationService::AllSources());
121 SpellcheckService::~SpellcheckService() {
122 // Remove pref observers
123 pref_change_registrar_.RemoveAll();
126 base::WeakPtr<SpellcheckService> SpellcheckService::GetWeakPtr() {
127 return weak_ptr_factory_.GetWeakPtr();
130 #if !defined(OS_MACOSX)
131 // static
132 size_t SpellcheckService::GetSpellCheckLanguages(
133 base::SupportsUserData* context,
134 std::vector<std::string>* languages) {
135 PrefService* prefs = user_prefs::UserPrefs::Get(context);
136 StringPrefMember accept_languages_pref;
137 accept_languages_pref.Init(prefs::kAcceptLanguages, prefs);
139 std::vector<std::string> accept_languages = base::SplitString(
140 accept_languages_pref.GetValue(), ",",
141 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
143 StringListPrefMember dictionaries_pref;
144 dictionaries_pref.Init(prefs::kSpellCheckDictionaries, prefs);
145 *languages = dictionaries_pref.GetValue();
146 size_t enabled_spellcheck_languages = languages->size();
148 for (std::vector<std::string>::const_iterator i = accept_languages.begin();
149 i != accept_languages.end(); ++i) {
150 std::string language =
151 chrome::spellcheck_common::GetCorrespondingSpellCheckLanguage(*i);
152 if (!language.empty() &&
153 std::find(languages->begin(), languages->end(), language) ==
154 languages->end()) {
155 languages->push_back(language);
159 return enabled_spellcheck_languages;
161 #endif // !OS_MACOSX
163 // static
164 bool SpellcheckService::SignalStatusEvent(
165 SpellcheckService::EventType status_type) {
166 DCHECK_CURRENTLY_ON(BrowserThread::UI);
168 if (!g_status_event)
169 return false;
170 g_status_type = status_type;
171 g_status_event->Signal();
172 return true;
175 void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled) {
176 metrics_.reset(new SpellCheckHostMetrics());
177 metrics_->RecordEnabledStats(spellcheck_enabled);
178 OnUseSpellingServiceChanged();
181 void SpellcheckService::InitForRenderer(content::RenderProcessHost* process) {
182 DCHECK_CURRENTLY_ON(BrowserThread::UI);
184 content::BrowserContext* context = process->GetBrowserContext();
185 if (SpellcheckServiceFactory::GetForContext(context) != this)
186 return;
188 PrefService* prefs = user_prefs::UserPrefs::Get(context);
189 std::vector<SpellCheckBDictLanguage> bdict_languages;
191 for (const auto& hunspell_dictionary : hunspell_dictionaries_) {
192 bdict_languages.push_back(SpellCheckBDictLanguage());
193 bdict_languages.back().language = hunspell_dictionary->GetLanguage();
194 bdict_languages.back().file =
195 hunspell_dictionary->GetDictionaryFile().IsValid()
196 ? IPC::GetFileHandleForProcess(
197 hunspell_dictionary->GetDictionaryFile().GetPlatformFile(),
198 process->GetHandle(), false)
199 : IPC::InvalidPlatformFileForTransit();
202 process->Send(new SpellCheckMsg_Init(
203 bdict_languages, custom_dictionary_->GetWords(),
204 prefs->GetBoolean(prefs::kEnableAutoSpellCorrect)));
205 process->Send(new SpellCheckMsg_EnableSpellCheck(
206 prefs->GetBoolean(prefs::kEnableContinuousSpellcheck)));
209 SpellCheckHostMetrics* SpellcheckService::GetMetrics() const {
210 return metrics_.get();
213 SpellcheckCustomDictionary* SpellcheckService::GetCustomDictionary() {
214 return custom_dictionary_.get();
217 const ScopedVector<SpellcheckHunspellDictionary>&
218 SpellcheckService::GetHunspellDictionaries() {
219 return hunspell_dictionaries_;
222 spellcheck::FeedbackSender* SpellcheckService::GetFeedbackSender() {
223 return feedback_sender_.get();
226 bool SpellcheckService::LoadExternalDictionary(std::string language,
227 std::string locale,
228 std::string path,
229 DictionaryFormat format) {
230 return false;
233 bool SpellcheckService::UnloadExternalDictionary(std::string path) {
234 return false;
237 void SpellcheckService::Observe(int type,
238 const content::NotificationSource& source,
239 const content::NotificationDetails& details) {
240 DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CREATED);
241 content::RenderProcessHost* process =
242 content::Source<content::RenderProcessHost>(source).ptr();
243 InitForRenderer(process);
246 void SpellcheckService::OnCustomDictionaryLoaded() {
247 InitForAllRenderers();
250 void SpellcheckService::OnCustomDictionaryChanged(
251 const SpellcheckCustomDictionary::Change& dictionary_change) {
252 for (content::RenderProcessHost::iterator i(
253 content::RenderProcessHost::AllHostsIterator());
254 !i.IsAtEnd(); i.Advance()) {
255 i.GetCurrentValue()->Send(new SpellCheckMsg_CustomDictionaryChanged(
256 dictionary_change.to_add(),
257 dictionary_change.to_remove()));
261 void SpellcheckService::OnHunspellDictionaryInitialized(
262 const std::string& language) {
263 InitForAllRenderers();
266 void SpellcheckService::OnHunspellDictionaryDownloadBegin(
267 const std::string& language) {
270 void SpellcheckService::OnHunspellDictionaryDownloadSuccess(
271 const std::string& language) {
274 void SpellcheckService::OnHunspellDictionaryDownloadFailure(
275 const std::string& language) {
278 // static
279 void SpellcheckService::AttachStatusEvent(base::WaitableEvent* status_event) {
280 DCHECK_CURRENTLY_ON(BrowserThread::UI);
282 g_status_event = status_event;
285 // static
286 SpellcheckService::EventType SpellcheckService::GetStatusEvent() {
287 DCHECK_CURRENTLY_ON(BrowserThread::UI);
288 return g_status_type;
291 void SpellcheckService::InitForAllRenderers() {
292 DCHECK_CURRENTLY_ON(BrowserThread::UI);
293 for (content::RenderProcessHost::iterator i(
294 content::RenderProcessHost::AllHostsIterator());
295 !i.IsAtEnd(); i.Advance()) {
296 content::RenderProcessHost* process = i.GetCurrentValue();
297 if (process && process->GetHandle())
298 InitForRenderer(process);
302 void SpellcheckService::OnEnableAutoSpellCorrectChanged() {
303 bool enabled = pref_change_registrar_.prefs()->GetBoolean(
304 prefs::kEnableAutoSpellCorrect);
305 for (content::RenderProcessHost::iterator i(
306 content::RenderProcessHost::AllHostsIterator());
307 !i.IsAtEnd(); i.Advance()) {
308 content::RenderProcessHost* process = i.GetCurrentValue();
309 process->Send(new SpellCheckMsg_EnableAutoSpellCorrect(enabled));
313 void SpellcheckService::OnSpellCheckDictionariesChanged() {
314 for (auto& hunspell_dictionary : hunspell_dictionaries_)
315 hunspell_dictionary->RemoveObserver(this);
317 PrefService* prefs = user_prefs::UserPrefs::Get(context_);
318 DCHECK(prefs);
320 const base::ListValue* dictionary_values =
321 prefs->GetList(prefs::kSpellCheckDictionaries);
323 hunspell_dictionaries_.clear();
324 for (const base::Value* dictionary_value : *dictionary_values) {
325 std::string dictionary;
326 dictionary_value->GetAsString(&dictionary);
327 hunspell_dictionaries_.push_back(new SpellcheckHunspellDictionary(
328 dictionary, context_->GetRequestContext(), this));
329 hunspell_dictionaries_.back()->AddObserver(this);
330 hunspell_dictionaries_.back()->Load();
333 std::string feedback_language;
334 dictionary_values->GetString(0, &feedback_language);
335 std::string language_code;
336 std::string country_code;
337 chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
338 feedback_language, &language_code, &country_code);
339 feedback_sender_->OnLanguageCountryChange(language_code, country_code);
340 UpdateFeedbackSenderState();
343 void SpellcheckService::OnUseSpellingServiceChanged() {
344 bool enabled = pref_change_registrar_.prefs()->GetBoolean(
345 prefs::kSpellCheckUseSpellingService);
346 if (metrics_)
347 metrics_->RecordSpellingServiceStats(enabled);
348 UpdateFeedbackSenderState();
351 void SpellcheckService::UpdateFeedbackSenderState() {
352 if (SpellingServiceClient::IsAvailable(
353 context_, SpellingServiceClient::SPELLCHECK)) {
354 feedback_sender_->StartFeedbackCollection();
355 } else {
356 feedback_sender_->StopFeedbackCollection();